@amerged/ohmyhost-cli 0.1.34 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @amerged/ohmyhost-cli
2
2
 
3
- ohmyho.st cli client, version 0.1.34.
3
+ ohmyho.st cli client, version 0.1.35.
4
4
 
5
5
  ```sh
6
6
  npm install --global @amerged/ohmyhost-cli
package/dist/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
3
3
  import {
4
4
  runProductionCli
5
- } from "./chunk-SRN6L765.js";
5
+ } from "./chunk-ZQSLRNH6.js";
6
6
  import "./chunk-7D2RFLWM.js";
7
7
 
8
8
  // apps/product-cli/dist/bin.js
@@ -34462,6 +34462,11 @@ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
34462
34462
  // packages/contracts/dist/framework-build-script.mjs
34463
34463
  var PINNED_BUN_VERSION = "1.2.22";
34464
34464
  var FRAMEWORK_VERSION_POLICY = Object.freeze({
34465
+ astro: Object.freeze({ floors: Object.freeze(["7.4.0"]), denied: Object.freeze([]) }),
34466
+ "@astrojs/cloudflare": Object.freeze({
34467
+ floors: Object.freeze(["14.4.0"]),
34468
+ denied: Object.freeze([])
34469
+ }),
34465
34470
  next: Object.freeze({ floors: Object.freeze(["15.5.27", "16.3.8"]), denied: Object.freeze([]) }),
34466
34471
  vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
34467
34472
  "@tanstack/react-start": Object.freeze({
@@ -34539,6 +34544,8 @@ var TYPESCRIPT_BUILD_STAGE = /^tsc(?:[ \t]+(?:--noEmit|-b|--build))?$/u;
34539
34544
  function isAdmittedFrameworkBuildScript(framework, script) {
34540
34545
  if (typeof script !== "string")
34541
34546
  return false;
34547
+ if (framework === "astro")
34548
+ return script === "astro build";
34542
34549
  if (framework === "nextjs")
34543
34550
  return script === "next build" || script === "next build --webpack";
34544
34551
  if (framework !== "vite" && framework !== "tanstack-start")
@@ -34728,11 +34735,15 @@ function frameworkVersionDiagnostic(admission, lockfile) {
34728
34735
  });
34729
34736
  }
34730
34737
  var FRAMEWORK_VERSION_PACKAGES = Object.freeze([
34738
+ "astro",
34739
+ "@astrojs/cloudflare",
34731
34740
  "next",
34732
34741
  "vite",
34733
34742
  "@tanstack/react-start"
34734
34743
  ]);
34735
34744
  var FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
34745
+ "astro",
34746
+ "@astrojs/cloudflare",
34736
34747
  "next",
34737
34748
  "react",
34738
34749
  "react-dom",
@@ -34743,10 +34754,23 @@ var FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
34743
34754
  "@cloudflare/vite-plugin"
34744
34755
  ]);
34745
34756
  var REACT_PACKAGES = Object.freeze(["react", "react-dom"]);
34746
- var BUILD_FRAMEWORKS = Object.freeze(["nextjs", "vite-static", "tanstack-start"]);
34757
+ var BUILD_FRAMEWORKS = Object.freeze([
34758
+ "astro",
34759
+ "nextjs",
34760
+ "vite-static",
34761
+ "tanstack-start"
34762
+ ]);
34747
34763
  var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
34748
34764
  var VITE_BUILD_SCRIPT = '"vite build", optionally joined with && to one "tsc", "tsc -b", "tsc --build" or "tsc --noEmit" stage';
34749
34765
  var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
34766
+ astro_template_import_unsupported: {
34767
+ paths: ["module"],
34768
+ template: '"{{path}}" uses a dynamic import, import.meta or require in an Astro template. Move server imports into frontmatter and dynamic browser imports into a normal statically imported browser module, then commit and push the change and plan the new commit.'
34769
+ },
34770
+ astro_config_unsupported: {
34771
+ paths: ["framework_config"],
34772
+ template: '"{{path}}" must use the standard Astro output directories and root base. For edge use output: "server", @astrojs/cloudflare with imageService: "passthrough", and session: false. Static hosting must omit a server adapter. Additional Cloudflare bindings, custom sessions and provider cache are not supported. Commit and push the change, then plan the new commit.'
34773
+ },
34750
34774
  managed_auth_database_required: {
34751
34775
  paths: ["repository_config"],
34752
34776
  template: managedCustomerAuthDatabaseMessage("{{path}}")
@@ -34821,7 +34845,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
34821
34845
  text: {
34822
34846
  nextjs: 'exactly "next build" or "next build --webpack"; ohmyho.st adds the OpenNext build itself',
34823
34847
  "vite-static": VITE_BUILD_SCRIPT,
34824
- "tanstack-start": VITE_BUILD_SCRIPT
34848
+ "tanstack-start": VITE_BUILD_SCRIPT,
34849
+ astro: 'exactly "astro build"'
34825
34850
  }
34826
34851
  }
34827
34852
  },
@@ -34837,11 +34862,11 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
34837
34862
  },
34838
34863
  framework_unsupported: {
34839
34864
  paths: ["manifest"],
34840
- template: `"{{path}}" declares none of next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`
34865
+ template: `"{{path}}" declares none of astro, next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`
34841
34866
  },
34842
34867
  framework_ambiguous: {
34843
34868
  paths: ["manifest"],
34844
- template: '"{{path}}" declares next together with vite or @tanstack/react-start, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.'
34869
+ template: '"{{path}}" declares competing application framework packages, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.'
34845
34870
  },
34846
34871
  lockfile_missing: {
34847
34872
  paths: ["manifest"],
@@ -34880,7 +34905,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
34880
34905
  text: {
34881
34906
  nextjs: "runtime.mode: edge",
34882
34907
  "vite-static": "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, storage or functions.crons is set, and static otherwise",
34883
- "tanstack-start": "runtime.mode: edge, or static without functions.crons and storage"
34908
+ "tanstack-start": "runtime.mode: edge, or static without functions.crons and storage",
34909
+ astro: "runtime.mode: static for prerendered sites, or edge with the Cloudflare adapter"
34884
34910
  }
34885
34911
  }
34886
34912
  },
@@ -34895,7 +34921,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
34895
34921
  text: {
34896
34922
  nextjs: ".open-next/assets",
34897
34923
  "vite-static": "dist",
34898
- "tanstack-start": "dist/client"
34924
+ "tanstack-start": "dist/client",
34925
+ astro: "dist for static, or dist/client for edge"
34899
34926
  }
34900
34927
  }
34901
34928
  },
@@ -34954,8 +34981,8 @@ var DETAIL_DEFINITIONS = FRAMEWORK_CONVERSION_DETAILS;
34954
34981
  var PLACEHOLDER = /\{\{([a-z]+)\}\}/gu;
34955
34982
  var PATH_PATTERN = "[A-Za-z0-9_@()[\\]./${}-]{1,240}";
34956
34983
  var SAFE_PATH = new RegExp(`^${PATH_PATTERN}$`, "u");
34957
- var MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:[cm]?[jt]sx?)$/u;
34958
- var FRAMEWORK_CONFIG_NAME = /^(?:next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
34984
+ var MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:astro|[cm]?[jt]sx?)$/u;
34985
+ var FRAMEWORK_CONFIG_NAME = /^(?:astro|next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
34959
34986
  var MAXIMUM_DETAIL_LENGTH = 2e3;
34960
34987
  var DETAIL_PATTERNS = Object.freeze(Object.entries(DETAIL_DEFINITIONS).map(([code, definition]) => Object.freeze({
34961
34988
  code,
@@ -40639,7 +40666,7 @@ function exact3(v, keys) {
40639
40666
  // apps/product-cli/package.json
40640
40667
  var package_default = {
40641
40668
  name: "@ohmyhost/product-cli",
40642
- version: "0.1.34",
40669
+ version: "0.1.35",
40643
40670
  private: true,
40644
40671
  ohmyhost: {
40645
40672
  deployment: "production",
@@ -47147,9 +47174,14 @@ var import_yaml2 = __toESM(require_dist(), 1);
47147
47174
 
47148
47175
  // packages/contracts/dist/framework-family.js
47149
47176
  function classifyFrameworkFamily(dependencies) {
47177
+ const astro = dependencies.has("astro");
47150
47178
  const next = dependencies.has("next");
47151
47179
  const tanStack = dependencies.has("@tanstack/react-start");
47152
47180
  const vite = dependencies.has("vite");
47181
+ if (astro && (next || tanStack))
47182
+ return "ambiguous";
47183
+ if (astro)
47184
+ return "astro";
47153
47185
  if (next && (tanStack || vite))
47154
47186
  return "ambiguous";
47155
47187
  if (next)
@@ -47175,6 +47207,7 @@ var DEPENDENCY_FIELDS = Object.freeze({
47175
47207
  any: Object.freeze(["dependencies", "devDependencies", "optionalDependencies"])
47176
47208
  });
47177
47209
  var REQUIRED_DEPENDENCIES = Object.freeze({
47210
+ astro: Object.freeze([["astro", "build"]]),
47178
47211
  nextjs: Object.freeze([
47179
47212
  ["next", "runtime"],
47180
47213
  ["react", "runtime"],
@@ -47193,6 +47226,7 @@ var REQUIRED_DEPENDENCIES = Object.freeze({
47193
47226
  });
47194
47227
  var TANSTACK_EDGE_DEPENDENCY = Object.freeze([CLOUDFLARE_PLUGIN, "build"]);
47195
47228
  var BUILD_SCRIPT_FRAMEWORKS = Object.freeze({
47229
+ astro: "astro",
47196
47230
  nextjs: "nextjs",
47197
47231
  "vite-static": "vite",
47198
47232
  "tanstack-start": "tanstack-start"
@@ -47201,7 +47235,11 @@ var BUILD_OUTPUTS = Object.freeze({
47201
47235
  "vite-static": "dist",
47202
47236
  "tanstack-start": "dist/client"
47203
47237
  });
47204
- var REQUIRED_CONFIG_PATHS = Object.freeze({ nextjs: "next.config.ts", "tanstack-start": "vite.config.ts" });
47238
+ var REQUIRED_CONFIG_PATHS = Object.freeze({
47239
+ astro: "astro.config.mjs",
47240
+ nextjs: "next.config.ts",
47241
+ "tanstack-start": "vite.config.ts"
47242
+ });
47205
47243
  var POSTCSS_CONFIG_PATH = /^(?:postcss\.config\.(?:[cm]?js|json)|\.postcssrc\.(?:js|json))$/u;
47206
47244
  var TAILWIND_TURBOPACK_LOADER = "@tailwindcss/turbopack";
47207
47245
  var MAXIMUM_LOCKFILE_LENGTH = 5 * 1024 * 1024;
@@ -47263,7 +47301,7 @@ function viteCompanionRequired(configuration) {
47263
47301
  function frameworkVersionAdmissions(framework, manifest2, lockfile) {
47264
47302
  const admissions = [];
47265
47303
  let locked;
47266
- for (const [name, placement] of REQUIRED_DEPENDENCIES[framework]) {
47304
+ for (const [name, placement] of framework === "astro" && declaredDependency(manifest2, "@astrojs/cloudflare", "build") !== null ? [...REQUIRED_DEPENDENCIES.astro, ["@astrojs/cloudflare", "build"]] : REQUIRED_DEPENDENCIES[framework]) {
47267
47305
  if (!isFrameworkVersionPackage(name))
47268
47306
  continue;
47269
47307
  const declared = declaredDependency(manifest2, name, placement);
@@ -47451,14 +47489,14 @@ function sourceWithoutComments(source) {
47451
47489
  return result;
47452
47490
  }
47453
47491
  function isDatabaseContractModule(path2) {
47454
- return /\.(?:ts|tsx|js|jsx|mjs|mts)$/u.test(path2) && !/\.d\.[cm]?ts$/u.test(path2);
47492
+ return /\.(?:astro|ts|tsx|js|jsx|mjs|mts)$/u.test(path2) && !/\.d\.[cm]?ts$/u.test(path2);
47455
47493
  }
47456
47494
  function manifestFindings(input, versions) {
47457
47495
  const { configuration, framework, manifest: manifest2 } = input;
47458
47496
  const findings = [];
47459
47497
  if (framework !== "functions" && !isAdmittedFrameworkBuildScript(BUILD_SCRIPT_FRAMEWORKS[framework], ownValue(manifest2["scripts"], "build")))
47460
47498
  findings.push(diagnostic({ code: "build_script_unsupported", path: PACKAGE_MANIFEST2, framework }));
47461
- const required = framework === "tanstack-start" && configuration.runtime.mode === "edge" ? [...REQUIRED_DEPENDENCIES[framework], TANSTACK_EDGE_DEPENDENCY] : REQUIRED_DEPENDENCIES[framework];
47499
+ const required = framework === "astro" && configuration.runtime.mode === "edge" ? [...REQUIRED_DEPENDENCIES.astro, ["@astrojs/cloudflare", "build"]] : framework === "tanstack-start" && configuration.runtime.mode === "edge" ? [...REQUIRED_DEPENDENCIES[framework], TANSTACK_EDGE_DEPENDENCY] : REQUIRED_DEPENDENCIES[framework];
47462
47500
  for (const [name, placement] of required)
47463
47501
  if (declaredDependency(manifest2, name, placement) === null)
47464
47502
  findings.push(diagnostic({
@@ -47492,14 +47530,14 @@ function configurationFindings(input) {
47492
47530
  findings.push(diagnostic({ code: "runtime_mode_unsupported", path: REPOSITORY_CONFIG, framework }));
47493
47531
  if (build !== null && framework !== "nextjs" && build.ssg_cache_max_mib !== void 0)
47494
47532
  findings.push(diagnostic({ code: "build_option_unsupported", path: REPOSITORY_CONFIG }));
47495
- const output = BUILD_OUTPUTS[framework];
47533
+ const output = framework === "astro" ? configuration.runtime.mode === "edge" ? "dist/client" : "dist" : BUILD_OUTPUTS[framework];
47496
47534
  if (build !== null && output !== void 0 && build.output !== output)
47497
47535
  findings.push(diagnostic({ code: "build_output_unsupported", path: REPOSITORY_CONFIG, framework }));
47498
47536
  if (packageManager !== null && framework !== "nextjs" && (configuration.build.install !== packageManager.installCommand || build !== null && build.command !== packageManager.buildCommand))
47499
47537
  findings.push(diagnostic({ code: "build_command_mismatch", path: REPOSITORY_CONFIG }));
47500
47538
  if (framework === "vite-static" && viteCompanionRequired(configuration) && !files.includes(VITE_COMPANION_ENTRY_PATH))
47501
47539
  findings.push(diagnostic({ code: "companion_entry_required", path: VITE_COMPANION_ENTRY_PATH }));
47502
- if ((framework === "functions" || crons && (framework === "nextjs" || framework === "tanstack-start" && configuration.runtime.mode === "edge")) && !files.includes(WORKER_MODULE_PATH))
47540
+ if ((framework === "functions" || crons && (framework === "nextjs" || framework === "astro" && configuration.runtime.mode === "edge" || framework === "tanstack-start" && configuration.runtime.mode === "edge")) && !files.includes(WORKER_MODULE_PATH))
47503
47541
  findings.push(diagnostic({ code: "worker_module_required", path: WORKER_MODULE_PATH }));
47504
47542
  if (managedCustomerAuthAdmissionIssue(configuration) !== null)
47505
47543
  findings.push(diagnostic({ code: "managed_auth_database_required", path: REPOSITORY_CONFIG }));
@@ -47507,6 +47545,8 @@ function configurationFindings(input) {
47507
47545
  }
47508
47546
  function runtimeModeAdmitted(framework, configuration, crons) {
47509
47547
  const mode = configuration.runtime.mode;
47548
+ if (framework === "astro")
47549
+ return mode === "edge" || mode === "static" && !crons && configuration.storage === void 0 && configuration.database?.enabled !== true && configuration.mail?.enabled !== true && configuration.auth?.provider !== "better-auth";
47510
47550
  if (framework === "nextjs")
47511
47551
  return mode === "edge";
47512
47552
  if (framework === "vite-static")
@@ -47521,6 +47561,8 @@ function frameworkConfigFindings(input) {
47521
47561
  if (frameworkConfig === null || framework === "functions")
47522
47562
  return [];
47523
47563
  const { path: path2, text } = frameworkConfig;
47564
+ if (framework === "astro")
47565
+ return astroConfigFindings(text, path2, input.configuration.runtime.mode);
47524
47566
  if (framework === "nextjs")
47525
47567
  return nextConfigFindings(text, path2, hasPostcssConfiguration(input));
47526
47568
  if (framework === "vite-static")
@@ -47545,7 +47587,7 @@ function repositoryConfiguration(text) {
47545
47587
  function frameworkConfigPath(framework, files, findings) {
47546
47588
  if (framework === "functions")
47547
47589
  return null;
47548
- const path2 = admitted(findings, () => resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files));
47590
+ const path2 = admitted(findings, () => resolveFrameworkConfig(framework === "astro" ? "astro.config" : framework === "nextjs" ? "next.config" : "vite.config", files));
47549
47591
  if (framework === "nextjs")
47550
47592
  admitted(findings, () => resolveFrameworkConfig("open-next.config", files));
47551
47593
  const required = REQUIRED_CONFIG_PATHS[framework];
@@ -47847,6 +47889,15 @@ function hasNamedImport(tokens, name, moduleName) {
47847
47889
  function hasModuleImport(tokens, moduleName) {
47848
47890
  return tokens.some((token, index) => token.value === "from" && tokens[index + 1]?.kind === "string" && tokens[index + 1]?.value === moduleName);
47849
47891
  }
47892
+ function astroConfigFindings(text, path2, mode) {
47893
+ const lexed = lexFrameworkConfig(text, path2);
47894
+ if ("finding" in lexed)
47895
+ return [lexed.finding];
47896
+ const tokens = lexed.tokens;
47897
+ const hasPropertyKey = (items, name) => items.some((token, index) => token.value === name && items[index + 1]?.value === ":");
47898
+ const invalid6 = hasNonRootProperty(tokens, ["base"], ["", "/"]) || ["srcDir", "outDir", "serverEntry", "client", "server", "assetsPrefix"].some((name) => hasPropertyKey(tokens, name)) || (mode === "edge" ? !text.includes("@astrojs/cloudflare") || !hasProperty(tokens, "output", "server") || !hasProperty(tokens, "session", "false") || !hasProperty(tokens, "imageService", "passthrough") || hasPropertyKey(tokens, "cache") : text.includes("@astrojs/cloudflare") || hasPropertyKey(tokens, "adapter") || hasProperty(tokens, "output", "server"));
47899
+ return invalid6 ? [diagnostic({ code: "astro_config_unsupported", path: path2 })] : [];
47900
+ }
47850
47901
 
47851
47902
  // packages/contracts/dist/database-migration-errors.js
47852
47903
  var DatabaseMigrationAdmissionError = class extends Error {
@@ -48299,7 +48350,7 @@ async function initializeRepository(input) {
48299
48350
  const match = path2.match(/^supabase\/functions\/([^/]+)\//u);
48300
48351
  return match?.[1] === void 0 || match[1].startsWith("_") ? [] : [match[1]];
48301
48352
  }));
48302
- const apiRouteCount = applicationFiles.filter((path2) => /^(?:src\/)?app\/api\/.+\/route\.(?:js|jsx|ts|tsx)$/u.test(path2)).length;
48353
+ const apiRouteCount = applicationFiles.filter((path2) => /^(?:src\/)?app\/api\/.+\/route\.(?:js|jsx|ts|tsx)$/u.test(path2) || framework === "astro" && /^src\/pages\/.+\.(?:js|ts)$/u.test(path2) && !path2.endsWith(".d.ts")).length;
48303
48354
  const supabase = dependencies.has("@supabase/supabase-js") || dependencies.has("@supabase/ssr");
48304
48355
  const configuredDatabase = existingConfiguration?.database?.enabled === true;
48305
48356
  const databaseProvider = supabaseMigrationCount > 0 ? "supabase" : postgresMigrationCount > 0 || configuredDatabase ? "postgresql" : "none";
@@ -48339,14 +48390,14 @@ async function initializeRepository(input) {
48339
48390
  const targetBetterAuth = existingConfiguration?.auth?.provider === "better-auth";
48340
48391
  const targetMail = existingConfiguration?.mail?.enabled === true;
48341
48392
  const targetStorage = r2 || supabaseStorage;
48342
- const tanStackRuntime = framework !== "tanstack-start" ? null : targetStorage ? "edge" : proposedTanStackRuntime(frameworkConfig, dependencies);
48393
+ const tanStackRuntime = framework !== "tanstack-start" && framework !== "astro" ? null : targetStorage ? "edge" : framework === "astro" ? dependencies.has("@astrojs/cloudflare") ? "edge" : "static" : proposedTanStackRuntime(frameworkConfig, dependencies);
48343
48394
  const companionRequired = framework === "vite" && (existingConfiguration === null ? databaseProvider !== "none" || targetBetterAuth || targetMail || targetStorage || edgeFunctionNames.size > 0 || applicationFiles.includes(VITE_COMPANION_ENTRY_PATH) : viteCompanionRequired(existingConfiguration));
48344
48395
  const generatedConfigurationYaml = renderConfiguration({
48345
48396
  project,
48346
48397
  framework,
48347
48398
  packageManager,
48348
48399
  databaseProvider,
48349
- hasHealthcheck: applicationFiles.includes("src/app/api/health/route.ts") || applicationFiles.includes("app/api/health/route.ts"),
48400
+ hasHealthcheck: applicationFiles.includes("src/app/api/health/route.ts") || applicationFiles.includes("app/api/health/route.ts") || framework === "astro" && applicationFiles.some((path2) => /^src\/pages\/api\/health(?:\/index)?\.(?:js|ts)$/u.test(path2)),
48350
48401
  mailEnabled: targetMail,
48351
48402
  betterAuthEnabled: targetBetterAuth,
48352
48403
  storageEnabled: targetStorage,
@@ -48464,7 +48515,7 @@ function requiredCatalogBlocker(diagnostic2, applicationRoot) {
48464
48515
  async function workerModuleBlockers(context) {
48465
48516
  const { config, framework, files } = context;
48466
48517
  const mode = config?.runtime.mode;
48467
- const scheduled = (config?.functions?.crons.length ?? 0) > 0 && (mode === "edge" && (framework === "vite" || framework === "nextjs" || framework === "tanstack-start") || mode === "functions" && framework === "functions");
48518
+ const scheduled = (config?.functions?.crons.length ?? 0) > 0 && (mode === "edge" && (framework === "astro" || framework === "vite" || framework === "nextjs" || framework === "tanstack-start") || mode === "functions" && framework === "functions");
48468
48519
  const path2 = framework === "vite" ? VITE_COMPANION_ENTRY_PATH : WORKER_MODULE_PATH;
48469
48520
  if (framework !== "functions" && !scheduled || !files.includes(path2))
48470
48521
  return [];
@@ -48737,7 +48788,7 @@ function hasDirectSafeBuildCommand(manifest2, framework) {
48737
48788
  const scripts = manifest2["scripts"];
48738
48789
  if (!isRecord5(scripts) || typeof scripts["build"] !== "string")
48739
48790
  return false;
48740
- return (framework === "nextjs" || framework === "vite" || framework === "tanstack-start") && isAdmittedFrameworkBuildScript(framework, scripts["build"]);
48791
+ return (framework === "astro" || framework === "nextjs" || framework === "vite" || framework === "tanstack-start") && isAdmittedFrameworkBuildScript(framework, scripts["build"]);
48741
48792
  }
48742
48793
  async function assertRepositoryRoot(root) {
48743
48794
  let information;
@@ -48843,11 +48894,11 @@ async function readResolvingLockfile(applicationDirectory, admission) {
48843
48894
  return text === null ? null : Object.freeze({ name: admission.lockfile, text });
48844
48895
  }
48845
48896
  async function readFrameworkConfig(applicationDirectory, files, framework) {
48846
- if (framework !== "nextjs" && framework !== "vite" && framework !== "tanstack-start")
48897
+ if (framework !== "astro" && framework !== "nextjs" && framework !== "vite" && framework !== "tanstack-start")
48847
48898
  return null;
48848
48899
  let path2;
48849
48900
  try {
48850
- path2 = resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files);
48901
+ path2 = resolveFrameworkConfig(framework === "astro" ? "astro.config" : framework === "nextjs" ? "next.config" : "vite.config", files);
48851
48902
  } catch (error) {
48852
48903
  if (error instanceof FrameworkAdmissionError)
48853
48904
  return null;
@@ -48913,7 +48964,7 @@ function renderConfiguration(input) {
48913
48964
  let output = "dist";
48914
48965
  if (input.framework === "nextjs")
48915
48966
  output = ".open-next/assets";
48916
- else if (input.framework === "tanstack-start")
48967
+ else if (input.framework === "tanstack-start" || input.framework === "astro" && input.tanStackRuntime === "edge")
48917
48968
  output = "dist/client";
48918
48969
  let mode = "auto";
48919
48970
  if (input.framework === "functions")
package/dist/index.js CHANGED
@@ -99,7 +99,7 @@ import {
99
99
  selectWorkspace,
100
100
  verifiedUserId,
101
101
  workspaceOf
102
- } from "./chunk-SRN6L765.js";
102
+ } from "./chunk-ZQSLRNH6.js";
103
103
  import {
104
104
  CredentialStoreIntegrityError,
105
105
  CredentialStoreUnavailableError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amerged/ohmyhost-cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/product-cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -310,8 +310,12 @@ export async function initializeRepository(
310
310
  return match?.[1] === undefined || match[1].startsWith("_") ? [] : [match[1]];
311
311
  }),
312
312
  );
313
- const apiRouteCount = applicationFiles.filter((path) =>
314
- /^(?:src\/)?app\/api\/.+\/route\.(?:js|jsx|ts|tsx)$/u.test(path),
313
+ const apiRouteCount = applicationFiles.filter(
314
+ (path) =>
315
+ /^(?:src\/)?app\/api\/.+\/route\.(?:js|jsx|ts|tsx)$/u.test(path) ||
316
+ (framework === "astro" &&
317
+ /^src\/pages\/.+\.(?:js|ts)$/u.test(path) &&
318
+ !path.endsWith(".d.ts")),
315
319
  ).length;
316
320
  const supabase = dependencies.has("@supabase/supabase-js") || dependencies.has("@supabase/ssr");
317
321
  const configuredDatabase = existingConfiguration?.database?.enabled === true;
@@ -368,11 +372,15 @@ export async function initializeRepository(
368
372
  const targetStorage = r2 || supabaseStorage;
369
373
  // TanStack Start binds storage only on edge, as a Vite app does through its companion.
370
374
  const tanStackRuntime =
371
- framework !== "tanstack-start"
375
+ framework !== "tanstack-start" && framework !== "astro"
372
376
  ? null
373
377
  : targetStorage
374
378
  ? "edge"
375
- : proposedTanStackRuntime(frameworkConfig, dependencies);
379
+ : framework === "astro"
380
+ ? dependencies.has("@astrojs/cloudflare")
381
+ ? "edge"
382
+ : "static"
383
+ : proposedTanStackRuntime(frameworkConfig, dependencies);
376
384
  const companionRequired =
377
385
  framework === "vite" &&
378
386
  (existingConfiguration === null
@@ -390,7 +398,11 @@ export async function initializeRepository(
390
398
  databaseProvider,
391
399
  hasHealthcheck:
392
400
  applicationFiles.includes("src/app/api/health/route.ts") ||
393
- applicationFiles.includes("app/api/health/route.ts"),
401
+ applicationFiles.includes("app/api/health/route.ts") ||
402
+ (framework === "astro" &&
403
+ applicationFiles.some((path) =>
404
+ /^src\/pages\/api\/health(?:\/index)?\.(?:js|ts)$/u.test(path),
405
+ )),
394
406
  mailEnabled: targetMail,
395
407
  betterAuthEnabled: targetBetterAuth,
396
408
  storageEnabled: targetStorage,
@@ -596,7 +608,10 @@ async function workerModuleBlockers(context: SourceCheckContext): Promise<Reposi
596
608
  const scheduled =
597
609
  (config?.functions?.crons.length ?? 0) > 0 &&
598
610
  ((mode === "edge" &&
599
- (framework === "vite" || framework === "nextjs" || framework === "tanstack-start")) ||
611
+ (framework === "astro" ||
612
+ framework === "vite" ||
613
+ framework === "nextjs" ||
614
+ framework === "tanstack-start")) ||
600
615
  (mode === "functions" && framework === "functions"));
601
616
  const path = framework === "vite" ? VITE_COMPANION_ENTRY_PATH : WORKER_MODULE_PATH;
602
617
  if ((framework !== "functions" && !scheduled) || !files.includes(path)) return [];
@@ -989,7 +1004,10 @@ function hasDirectSafeBuildCommand(
989
1004
  const scripts = manifest["scripts"];
990
1005
  if (!isRecord(scripts) || typeof scripts["build"] !== "string") return false;
991
1006
  return (
992
- (framework === "nextjs" || framework === "vite" || framework === "tanstack-start") &&
1007
+ (framework === "astro" ||
1008
+ framework === "nextjs" ||
1009
+ framework === "vite" ||
1010
+ framework === "tanstack-start") &&
993
1011
  isAdmittedFrameworkBuildScript(framework, scripts["build"])
994
1012
  );
995
1013
  }
@@ -1144,10 +1162,23 @@ async function readFrameworkConfig(
1144
1162
  files: readonly string[],
1145
1163
  framework: RepositoryFramework,
1146
1164
  ): Promise<FrameworkConfigText | null> {
1147
- if (framework !== "nextjs" && framework !== "vite" && framework !== "tanstack-start") return null;
1165
+ if (
1166
+ framework !== "astro" &&
1167
+ framework !== "nextjs" &&
1168
+ framework !== "vite" &&
1169
+ framework !== "tanstack-start"
1170
+ )
1171
+ return null;
1148
1172
  let path: string | null;
1149
1173
  try {
1150
- path = resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files);
1174
+ path = resolveFrameworkConfig(
1175
+ framework === "astro"
1176
+ ? "astro.config"
1177
+ : framework === "nextjs"
1178
+ ? "next.config"
1179
+ : "vite.config",
1180
+ files,
1181
+ );
1151
1182
  } catch (error) {
1152
1183
  // The shared rules name the ambiguous variants; none of them is judged.
1153
1184
  if (error instanceof FrameworkAdmissionError) return null;
@@ -1266,7 +1297,11 @@ function renderConfiguration(input: {
1266
1297
 
1267
1298
  let output = "dist";
1268
1299
  if (input.framework === "nextjs") output = ".open-next/assets";
1269
- else if (input.framework === "tanstack-start") output = "dist/client";
1300
+ else if (
1301
+ input.framework === "tanstack-start" ||
1302
+ (input.framework === "astro" && input.tanStackRuntime === "edge")
1303
+ )
1304
+ output = "dist/client";
1270
1305
 
1271
1306
  let mode = "auto";
1272
1307
  if (input.framework === "functions") mode = "functions";
@@ -19,7 +19,11 @@ export type LockfileName =
19
19
  | "package-lock.json"
20
20
  | "pnpm-lock.yaml"
21
21
  | "yarn.lock";
22
- export type FrameworkConfigBasename = "next.config" | "open-next.config" | "vite.config";
22
+ export type FrameworkConfigBasename =
23
+ | "astro.config"
24
+ | "next.config"
25
+ | "open-next.config"
26
+ | "vite.config";
23
27
 
24
28
  export type FrameworkAdmissionReason =
25
29
  | "ambiguous_config"
@@ -311,11 +315,15 @@ interface DetailDefinition {
311
315
  }
312
316
 
313
317
  const FRAMEWORK_VERSION_PACKAGES = Object.freeze([
318
+ "astro",
319
+ "@astrojs/cloudflare",
314
320
  "next",
315
321
  "vite",
316
322
  "@tanstack/react-start",
317
323
  ] as const satisfies readonly FrameworkVersionPackage[]);
318
324
  const FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
325
+ "astro",
326
+ "@astrojs/cloudflare",
319
327
  "next",
320
328
  "react",
321
329
  "react-dom",
@@ -326,7 +334,12 @@ const FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
326
334
  "@cloudflare/vite-plugin",
327
335
  ] as const);
328
336
  const REACT_PACKAGES = Object.freeze(["react", "react-dom"] as const);
329
- const BUILD_FRAMEWORKS = Object.freeze(["nextjs", "vite-static", "tanstack-start"] as const);
337
+ const BUILD_FRAMEWORKS = Object.freeze([
338
+ "astro",
339
+ "nextjs",
340
+ "vite-static",
341
+ "tanstack-start",
342
+ ] as const);
330
343
 
331
344
  const PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
332
345
  const VITE_BUILD_SCRIPT =
@@ -335,6 +348,16 @@ const VITE_BUILD_SCRIPT =
335
348
  // Each sentence names the file, the rule and the exact change. Render and parse both derive from
336
349
  // the template, so a client maps a detail back to exactly one typed diagnostic.
337
350
  const FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
351
+ astro_template_import_unsupported: {
352
+ paths: ["module"],
353
+ template:
354
+ '"{{path}}" uses a dynamic import, import.meta or require in an Astro template. Move server imports into frontmatter and dynamic browser imports into a normal statically imported browser module, then commit and push the change and plan the new commit.',
355
+ },
356
+ astro_config_unsupported: {
357
+ paths: ["framework_config"],
358
+ template:
359
+ '"{{path}}" must use the standard Astro output directories and root base. For edge use output: "server", @astrojs/cloudflare with imageService: "passthrough", and session: false. Static hosting must omit a server adapter. Additional Cloudflare bindings, custom sessions and provider cache are not supported. Commit and push the change, then plan the new commit.',
360
+ },
338
361
  managed_auth_database_required: {
339
362
  paths: ["repository_config"],
340
363
  template: managedCustomerAuthDatabaseMessage("{{path}}"),
@@ -417,6 +440,7 @@ const FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
417
440
  'exactly "next build" or "next build --webpack"; ohmyho.st adds the OpenNext build itself',
418
441
  "vite-static": VITE_BUILD_SCRIPT,
419
442
  "tanstack-start": VITE_BUILD_SCRIPT,
443
+ astro: 'exactly "astro build"',
420
444
  },
421
445
  },
422
446
  },
@@ -435,12 +459,12 @@ const FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
435
459
  },
436
460
  framework_unsupported: {
437
461
  paths: ["manifest"],
438
- template: `"{{path}}" declares none of next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`,
462
+ template: `"{{path}}" declares none of astro, next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`,
439
463
  },
440
464
  framework_ambiguous: {
441
465
  paths: ["manifest"],
442
466
  template:
443
- '"{{path}}" declares next together with vite or @tanstack/react-start, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.',
467
+ '"{{path}}" declares competing application framework packages, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.',
444
468
  },
445
469
  lockfile_missing: {
446
470
  paths: ["manifest"],
@@ -487,6 +511,7 @@ const FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
487
511
  "vite-static":
488
512
  "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, storage or functions.crons is set, and static otherwise",
489
513
  "tanstack-start": "runtime.mode: edge, or static without functions.crons and storage",
514
+ astro: "runtime.mode: static for prerendered sites, or edge with the Cloudflare adapter",
490
515
  },
491
516
  },
492
517
  },
@@ -502,6 +527,7 @@ const FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
502
527
  nextjs: ".open-next/assets",
503
528
  "vite-static": "dist",
504
529
  "tanstack-start": "dist/client",
530
+ astro: "dist for static, or dist/client for edge",
505
531
  },
506
532
  },
507
533
  },
@@ -585,8 +611,8 @@ const DETAIL_DEFINITIONS: Readonly<Record<FrameworkConversionDiagnosticCode, Det
585
611
  const PLACEHOLDER = /\{\{([a-z]+)\}\}/gu;
586
612
  const PATH_PATTERN = "[A-Za-z0-9_@()[\\]./${}-]{1,240}";
587
613
  const SAFE_PATH = new RegExp(`^${PATH_PATTERN}$`, "u");
588
- const MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:[cm]?[jt]sx?)$/u;
589
- const FRAMEWORK_CONFIG_NAME = /^(?:next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
614
+ const MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:astro|[cm]?[jt]sx?)$/u;
615
+ const FRAMEWORK_CONFIG_NAME = /^(?:astro|next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
590
616
  // The public problem detail bound; every rendered sentence stays far below it.
591
617
  const MAXIMUM_DETAIL_LENGTH = 2_000;
592
618
  const DETAIL_PATTERNS = Object.freeze(
@@ -1,7 +1,7 @@
1
1
  export const PINNED_BUN_VERSION = "1.2.22";
2
2
 
3
3
  /**
4
- * @typedef {"next" | "vite" | "@tanstack/react-start"} FrameworkVersionPackage
4
+ * @typedef {"next" | "vite" | "astro" | "@astrojs/cloudflare" | "@tanstack/react-start"} FrameworkVersionPackage
5
5
  * @typedef {Readonly<{
6
6
  * floors: readonly string[];
7
7
  * denied: readonly (readonly [string, string | null])[];
@@ -22,6 +22,11 @@ export const PINNED_BUN_VERSION = "1.2.22";
22
22
  * @type {FrameworkVersionPolicy}
23
23
  */
24
24
  export const FRAMEWORK_VERSION_POLICY = Object.freeze({
25
+ astro: Object.freeze({ floors: Object.freeze(["7.4.0"]), denied: Object.freeze([]) }),
26
+ "@astrojs/cloudflare": Object.freeze({
27
+ floors: Object.freeze(["14.4.0"]),
28
+ denied: Object.freeze([]),
29
+ }),
25
30
  next: Object.freeze({ floors: Object.freeze(["15.5.27", "16.3.8"]), denied: Object.freeze([]) }),
26
31
  vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
27
32
  "@tanstack/react-start": Object.freeze({
@@ -164,12 +169,13 @@ const TYPESCRIPT_BUILD_STAGE = /^tsc(?:[ \t]+(?:--noEmit|-b|--build))?$/u;
164
169
 
165
170
  /**
166
171
  * The same source is consumed by local admission, framework adapters and the build sandbox.
167
- * @param {"nextjs" | "tanstack-start" | "vite"} framework
172
+ * @param {"astro" | "nextjs" | "tanstack-start" | "vite"} framework
168
173
  * @param {unknown} script
169
174
  * @returns {boolean}
170
175
  */
171
176
  export function isAdmittedFrameworkBuildScript(framework, script) {
172
177
  if (typeof script !== "string") return false;
178
+ if (framework === "astro") return script === "astro build";
173
179
  if (framework === "nextjs") return script === "next build" || script === "next build --webpack";
174
180
  if (framework !== "vite" && framework !== "tanstack-start") return false;
175
181
  const stages = script.trim().split(/[ \t]*&&[ \t]*/u);
@@ -1,4 +1,5 @@
1
1
  export type FrameworkFamilyClassification =
2
+ | "astro"
2
3
  | "ambiguous"
3
4
  | "nextjs"
4
5
  | "tanstack-start"
@@ -8,9 +9,12 @@ export type FrameworkFamilyClassification =
8
9
  export function classifyFrameworkFamily(
9
10
  dependencies: ReadonlySet<string>,
10
11
  ): FrameworkFamilyClassification {
12
+ const astro = dependencies.has("astro");
11
13
  const next = dependencies.has("next");
12
14
  const tanStack = dependencies.has("@tanstack/react-start");
13
15
  const vite = dependencies.has("vite");
16
+ if (astro && (next || tanStack)) return "ambiguous";
17
+ if (astro) return "astro";
14
18
  if (next && (tanStack || vite)) return "ambiguous";
15
19
  if (next) return "nextjs";
16
20
  if (tanStack) return "tanstack-start";
@@ -40,7 +40,12 @@ export const WORKER_MODULE_PATH = "src/ohmyhost/worker.ts" as const;
40
40
  /** The Worker module of a Vite application with a server runtime. */
41
41
  export const VITE_COMPANION_ENTRY_PATH = "src/ohmyhost/companion.ts" as const;
42
42
 
43
- export type FrameworkSourceFramework = "functions" | "nextjs" | "tanstack-start" | "vite-static";
43
+ export type FrameworkSourceFramework =
44
+ | "astro"
45
+ | "functions"
46
+ | "nextjs"
47
+ | "tanstack-start"
48
+ | "vite-static";
44
49
  export type PackageManifest = Readonly<Record<string, unknown>>;
45
50
  /**
46
51
  * Where package.json may declare a dependency: `runtime` only in dependencies, `build` also in
@@ -91,6 +96,7 @@ const DEPENDENCY_FIELDS = Object.freeze({
91
96
  // Next.js and TanStack Start load their runtime packages in the server bundle, so package.json
92
97
  // declares them in dependencies; Vite and its plugins only build, so devDependencies suffice.
93
98
  const REQUIRED_DEPENDENCIES = Object.freeze({
99
+ astro: Object.freeze([["astro", "build"]] as const),
94
100
  nextjs: Object.freeze([
95
101
  ["next", "runtime"],
96
102
  ["react", "runtime"],
@@ -109,6 +115,7 @@ const REQUIRED_DEPENDENCIES = Object.freeze({
109
115
  } satisfies Record<FrameworkSourceFramework, readonly (readonly [string, DependencyPlacement])[]>);
110
116
  const TANSTACK_EDGE_DEPENDENCY = Object.freeze([CLOUDFLARE_PLUGIN, "build"] as const);
111
117
  const BUILD_SCRIPT_FRAMEWORKS = Object.freeze({
118
+ astro: "astro",
112
119
  nextjs: "nextjs",
113
120
  "vite-static": "vite",
114
121
  "tanstack-start": "tanstack-start",
@@ -118,7 +125,11 @@ const BUILD_OUTPUTS: Readonly<Partial<Record<FrameworkSourceFramework, string>>>
118
125
  "tanstack-start": "dist/client",
119
126
  });
120
127
  const REQUIRED_CONFIG_PATHS: Readonly<Partial<Record<FrameworkSourceFramework, string>>> =
121
- Object.freeze({ nextjs: "next.config.ts", "tanstack-start": "vite.config.ts" });
128
+ Object.freeze({
129
+ astro: "astro.config.mjs",
130
+ nextjs: "next.config.ts",
131
+ "tanstack-start": "vite.config.ts",
132
+ });
122
133
  // The PostCSS configuration the Next.js Webpack build reads: package.json "postcss" or one of these
123
134
  // files at the application root. It never reads a TypeScript one such as postcss.config.ts.
124
135
  const POSTCSS_CONFIG_PATH = /^(?:postcss\.config\.(?:[cm]?js|json)|\.postcssrc\.(?:js|json))$/u;
@@ -214,7 +225,10 @@ export function frameworkVersionAdmissions(
214
225
  ): readonly FrameworkVersionAdmission[] {
215
226
  const admissions: FrameworkVersionAdmission[] = [];
216
227
  let locked: ((packageName: string) => string | null) | undefined;
217
- for (const [name, placement] of REQUIRED_DEPENDENCIES[framework]) {
228
+ for (const [name, placement] of framework === "astro" &&
229
+ declaredDependency(manifest, "@astrojs/cloudflare", "build") !== null
230
+ ? [...REQUIRED_DEPENDENCIES.astro, ["@astrojs/cloudflare", "build"] as const]
231
+ : REQUIRED_DEPENDENCIES[framework]) {
218
232
  if (!isFrameworkVersionPackage(name)) continue;
219
233
  const declared = declaredDependency(manifest, name, placement);
220
234
  if (declared === null) continue;
@@ -504,7 +518,7 @@ export function sourceWithoutComments(source: string): string {
504
518
  * extensions planning reads, without TypeScript declaration files, which never run.
505
519
  */
506
520
  export function isDatabaseContractModule(path: string): boolean {
507
- return /\.(?:ts|tsx|js|jsx|mjs|mts)$/u.test(path) && !/\.d\.[cm]?ts$/u.test(path);
521
+ return /\.(?:astro|ts|tsx|js|jsx|mjs|mts)$/u.test(path) && !/\.d\.[cm]?ts$/u.test(path);
508
522
  }
509
523
 
510
524
  function manifestFindings(
@@ -524,9 +538,11 @@ function manifestFindings(
524
538
  diagnostic({ code: "build_script_unsupported", path: PACKAGE_MANIFEST, framework }),
525
539
  );
526
540
  const required =
527
- framework === "tanstack-start" && configuration.runtime.mode === "edge"
528
- ? [...REQUIRED_DEPENDENCIES[framework], TANSTACK_EDGE_DEPENDENCY]
529
- : REQUIRED_DEPENDENCIES[framework];
541
+ framework === "astro" && configuration.runtime.mode === "edge"
542
+ ? [...REQUIRED_DEPENDENCIES.astro, ["@astrojs/cloudflare", "build"] as const]
543
+ : framework === "tanstack-start" && configuration.runtime.mode === "edge"
544
+ ? [...REQUIRED_DEPENDENCIES[framework], TANSTACK_EDGE_DEPENDENCY]
545
+ : REQUIRED_DEPENDENCIES[framework];
530
546
  for (const [name, placement] of required)
531
547
  if (declaredDependency(manifest, name, placement) === null)
532
548
  findings.push(
@@ -567,7 +583,12 @@ function configurationFindings(input: FrameworkSourceRuleInput): FrameworkConver
567
583
  );
568
584
  if (build !== null && framework !== "nextjs" && build.ssg_cache_max_mib !== undefined)
569
585
  findings.push(diagnostic({ code: "build_option_unsupported", path: REPOSITORY_CONFIG }));
570
- const output = BUILD_OUTPUTS[framework];
586
+ const output =
587
+ framework === "astro"
588
+ ? configuration.runtime.mode === "edge"
589
+ ? "dist/client"
590
+ : "dist"
591
+ : BUILD_OUTPUTS[framework];
571
592
  if (build !== null && output !== undefined && build.output !== output)
572
593
  findings.push(
573
594
  diagnostic({ code: "build_output_unsupported", path: REPOSITORY_CONFIG, framework }),
@@ -592,6 +613,7 @@ function configurationFindings(input: FrameworkSourceRuleInput): FrameworkConver
592
613
  (framework === "functions" ||
593
614
  (crons &&
594
615
  (framework === "nextjs" ||
616
+ (framework === "astro" && configuration.runtime.mode === "edge") ||
595
617
  (framework === "tanstack-start" && configuration.runtime.mode === "edge")))) &&
596
618
  !files.includes(WORKER_MODULE_PATH)
597
619
  )
@@ -607,6 +629,16 @@ function runtimeModeAdmitted(
607
629
  crons: boolean,
608
630
  ): boolean {
609
631
  const mode = configuration.runtime.mode;
632
+ if (framework === "astro")
633
+ return (
634
+ mode === "edge" ||
635
+ (mode === "static" &&
636
+ !crons &&
637
+ configuration.storage === undefined &&
638
+ configuration.database?.enabled !== true &&
639
+ configuration.mail?.enabled !== true &&
640
+ configuration.auth?.provider !== "better-auth")
641
+ );
610
642
  if (framework === "nextjs") return mode === "edge";
611
643
  if (framework === "vite-static")
612
644
  return mode === (viteCompanionRequired(configuration) ? "edge" : "static");
@@ -629,6 +661,8 @@ function frameworkConfigFindings(
629
661
  const { frameworkConfig, framework } = input;
630
662
  if (frameworkConfig === null || framework === "functions") return [];
631
663
  const { path, text } = frameworkConfig;
664
+ if (framework === "astro")
665
+ return astroConfigFindings(text, path, input.configuration.runtime.mode);
632
666
  if (framework === "nextjs") return nextConfigFindings(text, path, hasPostcssConfiguration(input));
633
667
  if (framework === "vite-static") return viteConfigFindings(text, path);
634
668
  const mode = input.configuration.runtime.mode;
@@ -660,7 +694,14 @@ function frameworkConfigPath(
660
694
  ): string | null {
661
695
  if (framework === "functions") return null;
662
696
  const path = admitted(findings, () =>
663
- resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files),
697
+ resolveFrameworkConfig(
698
+ framework === "astro"
699
+ ? "astro.config"
700
+ : framework === "nextjs"
701
+ ? "next.config"
702
+ : "vite.config",
703
+ files,
704
+ ),
664
705
  );
665
706
  if (framework === "nextjs")
666
707
  admitted(findings, () => resolveFrameworkConfig("open-next.config", files));
@@ -1116,3 +1157,31 @@ function hasModuleImport(tokens: readonly ConfigToken[], moduleName: string): bo
1116
1157
  tokens[index + 1]?.value === moduleName,
1117
1158
  );
1118
1159
  }
1160
+
1161
+ /** Astro's build validates executable config; admission rejects known incompatible contracts. */
1162
+ function astroConfigFindings(
1163
+ text: string,
1164
+ path: string,
1165
+ mode: string,
1166
+ ): readonly FrameworkConversionDiagnostic[] {
1167
+ const lexed = lexFrameworkConfig(text, path);
1168
+ if ("finding" in lexed) return [lexed.finding];
1169
+ const tokens = lexed.tokens;
1170
+ const hasPropertyKey = (items: readonly ConfigToken[], name: string) =>
1171
+ items.some((token, index) => token.value === name && items[index + 1]?.value === ":");
1172
+ const invalid =
1173
+ hasNonRootProperty(tokens, ["base"], ["", "/"]) ||
1174
+ ["srcDir", "outDir", "serverEntry", "client", "server", "assetsPrefix"].some((name) =>
1175
+ hasPropertyKey(tokens, name),
1176
+ ) ||
1177
+ (mode === "edge"
1178
+ ? !text.includes("@astrojs/cloudflare") ||
1179
+ !hasProperty(tokens, "output", "server") ||
1180
+ !hasProperty(tokens, "session", "false") ||
1181
+ !hasProperty(tokens, "imageService", "passthrough") ||
1182
+ hasPropertyKey(tokens, "cache")
1183
+ : text.includes("@astrojs/cloudflare") ||
1184
+ hasPropertyKey(tokens, "adapter") ||
1185
+ hasProperty(tokens, "output", "server"));
1186
+ return invalid ? [diagnostic({ code: "astro_config_unsupported", path })] : [];
1187
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/sdk-ts",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {