@julien-lin/universal-pwa-core 1.3.14 → 1.3.16

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/dist/index.cjs CHANGED
@@ -102,9 +102,11 @@ __export(index_exports, {
102
102
  ConfigLoadError: () => ConfigLoadError,
103
103
  ConfigValidationError: () => ConfigValidationError,
104
104
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
105
+ DEFAULT_INJECTION_EXTENSIONS: () => DEFAULT_INJECTION_EXTENSIONS,
105
106
  DefaultBackendIntegrationFactory: () => DefaultBackendIntegrationFactory,
106
107
  DjangoIntegration: () => DjangoIntegration,
107
108
  FlaskIntegration: () => FlaskIntegration,
109
+ INJECTION_EXTENSIONS_BY_PROJECT_TYPE: () => INJECTION_EXTENSIONS_BY_PROJECT_TYPE,
108
110
  IconConfigSchema: () => IconConfigSchema,
109
111
  InjectionConfigSchema: () => InjectionConfigSchema,
110
112
  LaravelIntegration: () => LaravelIntegration,
@@ -120,6 +122,7 @@ __export(index_exports, {
120
122
  SymfonyIntegration: () => SymfonyIntegration,
121
123
  TelemetryCollector: () => TelemetryCollector,
122
124
  UniversalPWAConfigSchema: () => UniversalPWAConfigSchema,
125
+ WORDPRESS_INJECTION_PATTERNS: () => WORDPRESS_INJECTION_PATTERNS,
123
126
  buildDependencyGraph: () => buildDependencyGraph,
124
127
  checkHttps: () => checkHttps,
125
128
  checkProjectHttps: () => checkProjectHttps,
@@ -176,6 +179,7 @@ __export(index_exports, {
176
179
  injectMetaTagsInFile: () => injectMetaTagsInFile,
177
180
  injectMetaTagsInFilesBatch: () => injectMetaTagsInFilesBatch,
178
181
  loadConfig: () => loadConfig,
182
+ mapBackendManifestVarsToOptions: () => mapBackendManifestVarsToOptions,
179
183
  optimizeImage: () => optimizeImage,
180
184
  optimizeProject: () => optimizeProject,
181
185
  optimizeProjectImages: () => optimizeProjectImages,
@@ -1067,25 +1071,25 @@ function createResult(framework, confidence, indicators, version = null, configu
1067
1071
  configuration: finalConfig
1068
1072
  };
1069
1073
  }
1074
+ function hasFileAndDirectory(projectPath, file, dir) {
1075
+ return (0, import_fs.existsSync)((0, import_path.join)(projectPath, file)) && (0, import_fs.existsSync)((0, import_path.join)(projectPath, dir));
1076
+ }
1070
1077
  function detectFramework(projectPath) {
1071
1078
  const indicators = [];
1072
1079
  let framework = null;
1073
1080
  let confidence = "low";
1074
1081
  const projectConfig = detectProjectConfiguration(projectPath);
1075
- if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-config.php"))) {
1076
- indicators.push("wp-config.php");
1077
- if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-content"))) {
1078
- indicators.push("wp-content/");
1079
- if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-content", "plugins", "woocommerce"))) {
1080
- indicators.push("wp-content/plugins/woocommerce/");
1081
- framework = "woocommerce";
1082
- confidence = "high";
1083
- return createResult(framework, confidence, indicators, null, projectConfig);
1084
- }
1085
- framework = "wordpress";
1082
+ if (hasFileAndDirectory(projectPath, "wp-config.php", "wp-content")) {
1083
+ indicators.push("wp-config.php", "wp-content/");
1084
+ if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "wp-content", "plugins", "woocommerce"))) {
1085
+ indicators.push("wp-content/plugins/woocommerce/");
1086
+ framework = "woocommerce";
1086
1087
  confidence = "high";
1087
1088
  return createResult(framework, confidence, indicators, null, projectConfig);
1088
1089
  }
1090
+ framework = "wordpress";
1091
+ confidence = "high";
1092
+ return createResult(framework, confidence, indicators, null, projectConfig);
1089
1093
  }
1090
1094
  if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "sites")) && (0, import_fs.existsSync)((0, import_path.join)(projectPath, "modules"))) {
1091
1095
  indicators.push("sites/ and modules/ (Drupal)");
@@ -1096,14 +1100,11 @@ function detectFramework(projectPath) {
1096
1100
  return createResult(framework, confidence, indicators, null, projectConfig);
1097
1101
  }
1098
1102
  }
1099
- if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "configuration.php"))) {
1100
- indicators.push("configuration.php (Joomla)");
1101
- if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "administrator"))) {
1102
- indicators.push("administrator/");
1103
- framework = "joomla";
1104
- confidence = "high";
1105
- return createResult(framework, confidence, indicators, null, projectConfig);
1106
- }
1103
+ if (hasFileAndDirectory(projectPath, "configuration.php", "administrator")) {
1104
+ indicators.push("configuration.php (Joomla)", "administrator/");
1105
+ framework = "joomla";
1106
+ confidence = "high";
1107
+ return createResult(framework, confidence, indicators, null, projectConfig);
1107
1108
  }
1108
1109
  if ((0, import_fs.existsSync)((0, import_path.join)(projectPath, "theme.liquid")) || (0, import_fs.existsSync)((0, import_path.join)(projectPath, "config", "settings_schema.json"))) {
1109
1110
  indicators.push("theme.liquid or config/settings_schema.json (Shopify)");
@@ -2580,6 +2581,22 @@ var ManifestSchema = import_zod.z.object({
2580
2581
  prefer_related_applications: import_zod.z.boolean().optional(),
2581
2582
  related_applications: import_zod.z.array(import_zod.z.unknown()).optional()
2582
2583
  });
2584
+ function mapBackendManifestVarsToOptions(vars) {
2585
+ const out = {};
2586
+ if (typeof vars.name === "string") out.name = vars.name;
2587
+ if (typeof vars.short_name === "string") out.shortName = vars.short_name;
2588
+ if (typeof vars.description === "string") out.description = vars.description;
2589
+ if (typeof vars.start_url === "string") out.startUrl = vars.start_url;
2590
+ if (typeof vars.scope === "string") out.scope = vars.scope;
2591
+ if (typeof vars.display === "string")
2592
+ out.display = vars.display;
2593
+ if (typeof vars.theme_color === "string") out.themeColor = vars.theme_color;
2594
+ if (typeof vars.background_color === "string")
2595
+ out.backgroundColor = vars.background_color;
2596
+ if (typeof vars.orientation === "string")
2597
+ out.orientation = vars.orientation;
2598
+ return out;
2599
+ }
2583
2600
  function generateManifest(options) {
2584
2601
  let shortName = "PWA";
2585
2602
  if (options.shortName && typeof options.shortName === "string" && options.shortName.trim().length > 0) {
@@ -4660,7 +4677,9 @@ async function generateServiceWorker(options) {
4660
4677
  const { patterns: validatedPatterns, warnings: patternWarnings } = validateAndLimitPrecachePatterns(globPatterns, framework);
4661
4678
  (0, import_fs10.mkdirSync)(outputDir, { recursive: true });
4662
4679
  const finalTemplateType = templateType ?? (0, import_universal_pwa_templates.determineTemplateType)(architecture, framework ?? null);
4663
- const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(finalTemplateType);
4680
+ const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
4681
+ finalTemplateType
4682
+ );
4664
4683
  const swSrcPath = (0, import_path9.join)(outputDir, "sw-src.js");
4665
4684
  (0, import_fs10.writeFileSync)(swSrcPath, template.content, "utf-8");
4666
4685
  const swDestPath = (0, import_path9.join)(outputDir, swDest);
@@ -4756,7 +4775,7 @@ async function generateSimpleServiceWorker(options) {
4756
4775
  count: result.count,
4757
4776
  size: result.size,
4758
4777
  warnings: [...result.warnings ?? [], ...patternWarnings],
4759
- filePaths: (result.manifestEntries ?? []).map((entry) => typeof entry === "string" ? entry : entry.url)
4778
+ filePaths: result.filePaths ?? []
4760
4779
  };
4761
4780
  } catch (err) {
4762
4781
  const message = getErrorMessage(err);
@@ -4777,7 +4796,9 @@ async function generateServiceWorkerFromConfig(config, options) {
4777
4796
  swDest = "sw.js"
4778
4797
  } = options;
4779
4798
  const finalTemplateType = templateType ?? (0, import_universal_pwa_templates.determineTemplateType)(architecture, framework ?? null);
4780
- const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(finalTemplateType);
4799
+ const template = (0, import_universal_pwa_templates.getServiceWorkerTemplate)(
4800
+ finalTemplateType
4801
+ );
4781
4802
  const swSrcPath = (0, import_path9.join)(outputDir, "sw-src.js");
4782
4803
  (0, import_fs10.writeFileSync)(swSrcPath, template.content, "utf-8");
4783
4804
  const swDestPath = (0, import_path9.join)(outputDir, swDest);
@@ -6196,12 +6217,32 @@ var ManifestConfigSchema = import_zod2.z.object({
6196
6217
  /** Orientation */
6197
6218
  orientation: import_zod2.z.enum(["any", "natural", "landscape", "portrait", "portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]).optional()
6198
6219
  });
6220
+ var DEFAULT_INJECTION_EXTENSIONS = [
6221
+ "html",
6222
+ "twig",
6223
+ "html.twig",
6224
+ "blade.php",
6225
+ "jinja2",
6226
+ "j2",
6227
+ "html.j2"
6228
+ ];
6229
+ var INJECTION_EXTENSIONS_BY_PROJECT_TYPE = {
6230
+ php: ["html", "twig", "html.twig", "blade.php"],
6231
+ python: ["html", "jinja2", "j2", "html.j2"],
6232
+ static: ["html"]
6233
+ };
6234
+ var WORDPRESS_INJECTION_PATTERNS = [
6235
+ "**/wp-content/themes/**/header.php",
6236
+ "**/wp-content/themes/**/footer.php"
6237
+ ];
6199
6238
  var InjectionConfigSchema = import_zod2.z.object({
6200
6239
  /** Inject meta tags */
6201
6240
  inject: import_zod2.z.boolean().default(true),
6202
6241
  /** Maximum number of HTML files to process */
6203
6242
  maxFiles: import_zod2.z.number().positive().optional(),
6204
- /** HTML file patterns to include */
6243
+ /** File extensions for HTML/template injection (e.g. html, twig, blade.php, j2). Used to build glob pattern. */
6244
+ extensions: import_zod2.z.array(import_zod2.z.string()).optional(),
6245
+ /** HTML file patterns to include (overrides extensions if set) */
6205
6246
  include: import_zod2.z.array(import_zod2.z.string()).optional(),
6206
6247
  /** HTML file patterns to exclude */
6207
6248
  exclude: import_zod2.z.array(import_zod2.z.string()).optional()
@@ -6918,15 +6959,49 @@ var BaseBackendIntegration = class {
6918
6959
  };
6919
6960
 
6920
6961
  // src/backends/laravel.ts
6962
+ var import_node_fs8 = require("fs");
6963
+ var import_node_path8 = require("path");
6964
+
6965
+ // src/backends/spa-detector.ts
6921
6966
  var import_node_fs7 = require("fs");
6922
6967
  var import_node_path7 = require("path");
6968
+ var DEFAULT_VITE_INCLUDES = ["vue", "react"];
6969
+ var DEFAULT_PACKAGE_KEYS = ["vue", "react"];
6970
+ function detectSPAFromViteAndPackage(projectRoot, options = {}) {
6971
+ const viteIncludes = options.viteIncludes ?? DEFAULT_VITE_INCLUDES;
6972
+ const packageKeys = options.packageKeys ?? DEFAULT_PACKAGE_KEYS;
6973
+ try {
6974
+ const vitePath = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.ts")) ? (0, import_node_path7.join)(projectRoot, "vite.config.ts") : (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.js")) ? (0, import_node_path7.join)(projectRoot, "vite.config.js") : (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.mjs")) ? (0, import_node_path7.join)(projectRoot, "vite.config.mjs") : null;
6975
+ if (vitePath) {
6976
+ const viteContent = (0, import_node_fs7.readFileSync)(vitePath, "utf-8");
6977
+ if (viteIncludes.some((s) => viteContent.includes(s))) {
6978
+ return true;
6979
+ }
6980
+ }
6981
+ const packagePath = (0, import_node_path7.join)(projectRoot, "package.json");
6982
+ if ((0, import_node_fs7.existsSync)(packagePath)) {
6983
+ const content = (0, import_node_fs7.readFileSync)(packagePath, "utf-8");
6984
+ const pkg = JSON.parse(content);
6985
+ const deps = pkg.dependencies ?? {};
6986
+ const devDeps = pkg.devDependencies ?? {};
6987
+ if (packageKeys.some((key) => deps[key] !== void 0 || devDeps[key] !== void 0)) {
6988
+ return true;
6989
+ }
6990
+ }
6991
+ return false;
6992
+ } catch {
6993
+ return false;
6994
+ }
6995
+ }
6996
+
6997
+ // src/backends/laravel.ts
6923
6998
  function detectLaravelVersion(projectRoot) {
6924
6999
  try {
6925
- const composerPath = (0, import_node_path7.join)(projectRoot, "composer.json");
6926
- if (!(0, import_node_fs7.existsSync)(composerPath)) {
7000
+ const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7001
+ if (!(0, import_node_fs8.existsSync)(composerPath)) {
6927
7002
  return null;
6928
7003
  }
6929
- const content = (0, import_node_fs7.readFileSync)(composerPath, "utf-8");
7004
+ const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
6930
7005
  const composer = JSON.parse(content);
6931
7006
  const requires = composer.require;
6932
7007
  const laravelVersion = requires?.["laravel/framework"];
@@ -6946,11 +7021,11 @@ function detectLaravelVersion(projectRoot) {
6946
7021
  }
6947
7022
  function hasLaravelDependency(projectRoot) {
6948
7023
  try {
6949
- const composerPath = (0, import_node_path7.join)(projectRoot, "composer.json");
6950
- if (!(0, import_node_fs7.existsSync)(composerPath)) {
7024
+ const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7025
+ if (!(0, import_node_fs8.existsSync)(composerPath)) {
6951
7026
  return false;
6952
7027
  }
6953
- const content = (0, import_node_fs7.readFileSync)(composerPath, "utf-8");
7028
+ const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
6954
7029
  const composer = JSON.parse(content);
6955
7030
  const requires = composer.require;
6956
7031
  return !!requires?.["laravel/framework"];
@@ -6960,11 +7035,11 @@ function hasLaravelDependency(projectRoot) {
6960
7035
  }
6961
7036
  function hasLumenDependency(projectRoot) {
6962
7037
  try {
6963
- const composerPath = (0, import_node_path7.join)(projectRoot, "composer.json");
6964
- if (!(0, import_node_fs7.existsSync)(composerPath)) {
7038
+ const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7039
+ if (!(0, import_node_fs8.existsSync)(composerPath)) {
6965
7040
  return false;
6966
7041
  }
6967
- const content = (0, import_node_fs7.readFileSync)(composerPath, "utf-8");
7042
+ const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
6968
7043
  const composer = JSON.parse(content);
6969
7044
  const requires = composer.require;
6970
7045
  return !!(requires?.["laravel/lumen-framework"] || requires?.["lumen/framework"]);
@@ -6973,33 +7048,18 @@ function hasLumenDependency(projectRoot) {
6973
7048
  }
6974
7049
  }
6975
7050
  function detectSPAMode(projectRoot) {
6976
- try {
6977
- const viteExists = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.ts")) || (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.js")) || (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.mjs"));
6978
- if (viteExists) {
6979
- const vitePath = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.ts")) ? (0, import_node_path7.join)(projectRoot, "vite.config.ts") : (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "vite.config.js")) ? (0, import_node_path7.join)(projectRoot, "vite.config.js") : (0, import_node_path7.join)(projectRoot, "vite.config.mjs");
6980
- const viteContent = (0, import_node_fs7.readFileSync)(vitePath, "utf-8");
6981
- return viteContent.includes("vue") || viteContent.includes("react") || viteContent.includes("inertia");
6982
- }
6983
- const packagePath = (0, import_node_path7.join)(projectRoot, "package.json");
6984
- if ((0, import_node_fs7.existsSync)(packagePath)) {
6985
- const content = (0, import_node_fs7.readFileSync)(packagePath, "utf-8");
6986
- const pkg = JSON.parse(content);
6987
- const dependencies = pkg.dependencies;
6988
- const devDependencies = pkg.devDependencies;
6989
- return !!(dependencies?.vue || dependencies?.react || dependencies?.["@inertiajs/inertia"] || devDependencies?.vue || devDependencies?.react || devDependencies?.["@inertiajs/inertia"]);
6990
- }
6991
- return false;
6992
- } catch {
6993
- return false;
6994
- }
7051
+ return detectSPAFromViteAndPackage(projectRoot, {
7052
+ viteIncludes: ["vue", "react", "inertia"],
7053
+ packageKeys: ["vue", "react", "@inertiajs/inertia"]
7054
+ });
6995
7055
  }
6996
7056
  function detectLivewire(projectRoot) {
6997
7057
  try {
6998
- const composerPath = (0, import_node_path7.join)(projectRoot, "composer.json");
6999
- if (!(0, import_node_fs7.existsSync)(composerPath)) {
7058
+ const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7059
+ if (!(0, import_node_fs8.existsSync)(composerPath)) {
7000
7060
  return false;
7001
7061
  }
7002
- const content = (0, import_node_fs7.readFileSync)(composerPath, "utf-8");
7062
+ const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7003
7063
  const composer = JSON.parse(content);
7004
7064
  const requires = composer.require;
7005
7065
  return !!(requires?.["livewire/livewire"] || requires?.livewire);
@@ -7007,6 +7067,36 @@ function detectLivewire(projectRoot) {
7007
7067
  return false;
7008
7068
  }
7009
7069
  }
7070
+ function detectAlpine(projectRoot) {
7071
+ try {
7072
+ const packageJsonPath = (0, import_node_path8.join)(projectRoot, "package.json");
7073
+ if ((0, import_node_fs8.existsSync)(packageJsonPath)) {
7074
+ const content = (0, import_node_fs8.readFileSync)(packageJsonPath, "utf-8");
7075
+ const pkg = JSON.parse(content);
7076
+ const devDeps = pkg.devDependencies;
7077
+ const deps = pkg.dependencies;
7078
+ if (devDeps?.["alpinejs"] || deps?.["alpinejs"]) {
7079
+ return true;
7080
+ }
7081
+ }
7082
+ const resourcesPath = (0, import_node_path8.join)(projectRoot, "resources");
7083
+ if ((0, import_node_fs8.existsSync)(resourcesPath)) {
7084
+ try {
7085
+ const layoutFile = (0, import_node_path8.join)(resourcesPath, "views", "app.blade.php");
7086
+ if ((0, import_node_fs8.existsSync)(layoutFile)) {
7087
+ const content = (0, import_node_fs8.readFileSync)(layoutFile, "utf-8");
7088
+ if (/alpine|x-data|@click|x-show/i.test(content)) {
7089
+ return true;
7090
+ }
7091
+ }
7092
+ } catch {
7093
+ }
7094
+ }
7095
+ return false;
7096
+ } catch {
7097
+ return false;
7098
+ }
7099
+ }
7010
7100
  var LaravelIntegration = class extends BaseBackendIntegration {
7011
7101
  id = "laravel";
7012
7102
  name = "Laravel";
@@ -7018,6 +7108,8 @@ var LaravelIntegration = class extends BaseBackendIntegration {
7018
7108
  this.config = {
7019
7109
  projectRoot,
7020
7110
  csrfProtection: true,
7111
+ isLivewire: detectLivewire(projectRoot),
7112
+ isAlpine: detectAlpine(projectRoot),
7021
7113
  ...config
7022
7114
  };
7023
7115
  }
@@ -7037,12 +7129,12 @@ var LaravelIntegration = class extends BaseBackendIntegration {
7037
7129
  indicators: ["composer.json: laravel/lumen-framework"]
7038
7130
  };
7039
7131
  }
7040
- const hasComposerJson = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "composer.json"));
7041
- const hasArtisan = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "artisan"));
7042
- const hasConfig = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "config"));
7043
- const hasApp = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "app"));
7044
- const hasRoutes = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "routes"));
7045
- const hasEnvExample = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, ".env.example"));
7132
+ const hasComposerJson = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "composer.json"));
7133
+ const hasArtisan = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "artisan"));
7134
+ const hasConfig = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "config"));
7135
+ const hasApp = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "app"));
7136
+ const hasRoutes = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "routes"));
7137
+ const hasEnvExample = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, ".env.example"));
7046
7138
  if (!hasComposerJson || !hasLaravelDependency(projectRoot)) {
7047
7139
  return {
7048
7140
  detected: false,
@@ -7069,7 +7161,8 @@ var LaravelIntegration = class extends BaseBackendIntegration {
7069
7161
  hasRoutes && "routes/",
7070
7162
  hasEnvExample && ".env.example",
7071
7163
  isSPA && "SPA mode (Vue/React/Inertia)",
7072
- isLivewire && "Livewire"
7164
+ isLivewire && "Livewire",
7165
+ this.config.isAlpine && "Alpine.js"
7073
7166
  ].filter(Boolean);
7074
7167
  const indicatorCount = indicators.length;
7075
7168
  const confidence = indicatorCount >= 4 ? "high" : indicatorCount >= 2 ? "medium" : "low";
@@ -7088,6 +7181,7 @@ var LaravelIntegration = class extends BaseBackendIntegration {
7088
7181
  generateServiceWorkerConfig() {
7089
7182
  const isSPA = this.config.isSPA ?? false;
7090
7183
  const isLivewire = this.config.isLivewire ?? false;
7184
+ const isAlpine = this.config.isAlpine ?? false;
7091
7185
  const staticRoutes = [
7092
7186
  {
7093
7187
  pattern: "*.{js,css,woff,woff2,ttf,otf}",
@@ -7136,27 +7230,78 @@ var LaravelIntegration = class extends BaseBackendIntegration {
7136
7230
  description: "Image files"
7137
7231
  }
7138
7232
  ];
7139
- const customRoutes = isLivewire ? [
7140
- {
7233
+ const customRoutes = [];
7234
+ if (isLivewire) {
7235
+ customRoutes.push({
7141
7236
  pattern: "/livewire/**",
7142
7237
  strategy: {
7143
- ...PRESET_STRATEGIES.ApiEndpoints,
7144
- networkTimeoutSeconds: 5
7238
+ name: "NetworkFirst",
7239
+ cacheName: "laravel-livewire-cache",
7240
+ networkTimeoutSeconds: 5,
7241
+ expiration: {
7242
+ maxEntries: 50,
7243
+ maxAgeSeconds: 600
7244
+ // 10 minutes
7245
+ }
7145
7246
  },
7146
7247
  priority: 15,
7147
7248
  description: "Livewire AJAX endpoints"
7148
- }
7149
- ] : void 0;
7249
+ });
7250
+ }
7251
+ if (isAlpine) {
7252
+ customRoutes.push({
7253
+ pattern: "/alpine/**",
7254
+ strategy: {
7255
+ name: "NetworkFirst",
7256
+ cacheName: "laravel-alpine-cache",
7257
+ networkTimeoutSeconds: 3,
7258
+ expiration: {
7259
+ maxEntries: 30,
7260
+ maxAgeSeconds: 300
7261
+ // 5 minutes
7262
+ }
7263
+ },
7264
+ priority: 14,
7265
+ description: "Alpine.js component requests"
7266
+ });
7267
+ customRoutes.push({
7268
+ pattern: "/api/alpine/**",
7269
+ strategy: {
7270
+ name: "NetworkFirst",
7271
+ cacheName: "laravel-alpine-api-cache",
7272
+ networkTimeoutSeconds: 2,
7273
+ expiration: {
7274
+ maxEntries: 40,
7275
+ maxAgeSeconds: 300
7276
+ // 5 minutes
7277
+ }
7278
+ },
7279
+ priority: 14,
7280
+ description: "Alpine.js API endpoints"
7281
+ });
7282
+ }
7283
+ if (isLivewire && isAlpine) {
7284
+ customRoutes.push({
7285
+ pattern: "/*.x-**",
7286
+ strategy: {
7287
+ name: "NetworkFirst",
7288
+ cacheName: "laravel-interactive-cache",
7289
+ networkTimeoutSeconds: 4,
7290
+ expiration: {
7291
+ maxEntries: 50,
7292
+ maxAgeSeconds: 600
7293
+ }
7294
+ },
7295
+ priority: 16,
7296
+ description: "Interactive component requests (Livewire + Alpine)"
7297
+ });
7298
+ }
7150
7299
  return {
7151
7300
  destination: "public/sw.js",
7152
7301
  staticRoutes,
7153
7302
  apiRoutes,
7154
7303
  imageRoutes,
7155
- customRoutes,
7156
- offline: {
7157
- fallbackPage: "/offline",
7158
- fallbackImage: isSPA ? "/images/placeholder.png" : void 0
7159
- },
7304
+ customRoutes: customRoutes.length > 0 ? customRoutes : void 0,
7160
7305
  features: isSPA ? { hydration: true } : void 0
7161
7306
  };
7162
7307
  }
@@ -7254,7 +7399,23 @@ class PWAMiddleware
7254
7399
  * Get API pattern routes for intelligent caching
7255
7400
  */
7256
7401
  getApiPatterns() {
7257
- return ["/api/**", "/graphql", "/livewire/**"];
7402
+ const patterns = ["/api/**", "/graphql"];
7403
+ if (this.config.isLivewire) {
7404
+ patterns.push("/livewire/**");
7405
+ }
7406
+ if (this.config.isAlpine) {
7407
+ patterns.push("/alpine/**", "/api/alpine/**");
7408
+ }
7409
+ return patterns;
7410
+ }
7411
+ /**
7412
+ * Get interactive framework configuration (Livewire + Alpine)
7413
+ */
7414
+ getInteractiveFrameworks() {
7415
+ return {
7416
+ livewire: this.config.isLivewire ?? false,
7417
+ alpine: this.config.isAlpine ?? false
7418
+ };
7258
7419
  }
7259
7420
  /**
7260
7421
  * Get static asset patterns
@@ -7275,27 +7436,27 @@ class PWAMiddleware
7275
7436
  const warnings = [];
7276
7437
  const suggestions = [];
7277
7438
  const projectRoot = this.config.projectRoot;
7278
- if (!(0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "public/manifest.json"))) {
7439
+ if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/manifest.json"))) {
7279
7440
  warnings.push("manifest.json not found in public directory");
7280
7441
  suggestions.push("Run: `npm run build` to generate manifest.json");
7281
7442
  }
7282
- if (!(0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "public/sw.js"))) {
7443
+ if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/sw.js"))) {
7283
7444
  warnings.push("Service worker (sw.js) not found in public directory");
7284
7445
  suggestions.push("Run PWA generation tool to create service worker");
7285
7446
  }
7286
- const hasUrlRewrite = (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "public/.htaccess")) || (0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "public/web.config"));
7447
+ const hasUrlRewrite = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/.htaccess")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/web.config"));
7287
7448
  if (!hasUrlRewrite) {
7288
7449
  warnings.push("URL rewrite configuration not found");
7289
7450
  suggestions.push(
7290
7451
  "Ensure your web server properly routes requests to index.php"
7291
7452
  );
7292
7453
  }
7293
- if (!(0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "app/Http/Middleware/PWAMiddleware.php"))) {
7454
+ if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "app/Http/Middleware/PWAMiddleware.php"))) {
7294
7455
  suggestions.push(
7295
7456
  "Consider registering PWAMiddleware for optimal PWA support"
7296
7457
  );
7297
7458
  }
7298
- if (!(0, import_node_fs7.existsSync)((0, import_node_path7.join)(projectRoot, "resources/views/offline.blade.php"))) {
7459
+ if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "resources/views/offline.blade.php"))) {
7299
7460
  warnings.push("Offline fallback view not found");
7300
7461
  suggestions.push(
7301
7462
  "Create resources/views/offline.blade.php for offline experience"
@@ -7313,11 +7474,11 @@ class PWAMiddleware
7313
7474
  return process.env.APP_NAME;
7314
7475
  }
7315
7476
  try {
7316
- const composerPath = (0, import_node_path7.join)(this.config.projectRoot, "composer.json");
7317
- if (!(0, import_node_fs7.existsSync)(composerPath)) {
7477
+ const composerPath = (0, import_node_path8.join)(this.config.projectRoot, "composer.json");
7478
+ if (!(0, import_node_fs8.existsSync)(composerPath)) {
7318
7479
  return "Laravel App";
7319
7480
  }
7320
- const content = (0, import_node_fs7.readFileSync)(composerPath, "utf-8");
7481
+ const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7321
7482
  const composer = JSON.parse(content);
7322
7483
  const name = typeof composer.name === "string" ? composer.name : "";
7323
7484
  if (!name) {
@@ -7332,15 +7493,15 @@ class PWAMiddleware
7332
7493
  };
7333
7494
 
7334
7495
  // src/backends/symfony.ts
7335
- var import_node_fs8 = require("fs");
7336
- var import_node_path8 = require("path");
7496
+ var import_node_fs9 = require("fs");
7497
+ var import_node_path9 = require("path");
7337
7498
  function detectSymfonyVersion(projectRoot) {
7338
7499
  try {
7339
- const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7340
- if (!(0, import_node_fs8.existsSync)(composerPath)) {
7500
+ const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
7501
+ if (!(0, import_node_fs9.existsSync)(composerPath)) {
7341
7502
  return null;
7342
7503
  }
7343
- const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7504
+ const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
7344
7505
  const composer = JSON.parse(content);
7345
7506
  const requires = composer.require;
7346
7507
  const requiresDev = composer["require-dev"];
@@ -7361,11 +7522,11 @@ function detectSymfonyVersion(projectRoot) {
7361
7522
  }
7362
7523
  function hasSymfonyDependency(projectRoot) {
7363
7524
  try {
7364
- const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7365
- if (!(0, import_node_fs8.existsSync)(composerPath)) {
7525
+ const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
7526
+ if (!(0, import_node_fs9.existsSync)(composerPath)) {
7366
7527
  return false;
7367
7528
  }
7368
- const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7529
+ const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
7369
7530
  const composer = JSON.parse(content);
7370
7531
  const requires = composer.require;
7371
7532
  const requiresDev = composer["require-dev"];
@@ -7375,36 +7536,21 @@ function hasSymfonyDependency(projectRoot) {
7375
7536
  }
7376
7537
  }
7377
7538
  function detectSPAMode2(projectRoot) {
7378
- try {
7379
- if ((0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "webpack.config.js")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "webpack.config.ts"))) {
7380
- return true;
7381
- }
7382
- const viteExists = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.ts")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.js")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.mjs"));
7383
- if (viteExists) {
7384
- const vitePath = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.ts")) ? (0, import_node_path8.join)(projectRoot, "vite.config.ts") : (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "vite.config.js")) ? (0, import_node_path8.join)(projectRoot, "vite.config.js") : (0, import_node_path8.join)(projectRoot, "vite.config.mjs");
7385
- const viteContent = (0, import_node_fs8.readFileSync)(vitePath, "utf-8");
7386
- return viteContent.includes("vue") || viteContent.includes("react") || viteContent.includes("svelte");
7387
- }
7388
- const packagePath = (0, import_node_path8.join)(projectRoot, "package.json");
7389
- if ((0, import_node_fs8.existsSync)(packagePath)) {
7390
- const content = (0, import_node_fs8.readFileSync)(packagePath, "utf-8");
7391
- const pkg = JSON.parse(content);
7392
- const dependencies = pkg.dependencies;
7393
- const devDependencies = pkg.devDependencies;
7394
- return !!(dependencies?.vue || dependencies?.react || dependencies?.svelte || devDependencies?.vue || devDependencies?.react || devDependencies?.svelte);
7395
- }
7396
- return false;
7397
- } catch {
7398
- return false;
7539
+ if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "webpack.config.js")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "webpack.config.ts"))) {
7540
+ return true;
7399
7541
  }
7542
+ return detectSPAFromViteAndPackage(projectRoot, {
7543
+ viteIncludes: ["vue", "react", "svelte"],
7544
+ packageKeys: ["vue", "react", "svelte"]
7545
+ });
7400
7546
  }
7401
7547
  function detectAPIPlatform(projectRoot) {
7402
7548
  try {
7403
- const composerPath = (0, import_node_path8.join)(projectRoot, "composer.json");
7404
- if (!(0, import_node_fs8.existsSync)(composerPath)) {
7549
+ const composerPath = (0, import_node_path9.join)(projectRoot, "composer.json");
7550
+ if (!(0, import_node_fs9.existsSync)(composerPath)) {
7405
7551
  return false;
7406
7552
  }
7407
- const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7553
+ const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
7408
7554
  const composer = JSON.parse(content);
7409
7555
  const requires = composer.require;
7410
7556
  const requiresDev = composer["require-dev"];
@@ -7415,11 +7561,11 @@ function detectAPIPlatform(projectRoot) {
7415
7561
  }
7416
7562
  function parseEnv(projectRoot) {
7417
7563
  try {
7418
- const envPath = (0, import_node_path8.join)(projectRoot, ".env");
7419
- if (!(0, import_node_fs8.existsSync)(envPath)) {
7564
+ const envPath = (0, import_node_path9.join)(projectRoot, ".env");
7565
+ if (!(0, import_node_fs9.existsSync)(envPath)) {
7420
7566
  return {};
7421
7567
  }
7422
- const content = (0, import_node_fs8.readFileSync)(envPath, "utf-8");
7568
+ const content = (0, import_node_fs9.readFileSync)(envPath, "utf-8");
7423
7569
  const env = {};
7424
7570
  content.split("\n").forEach((line) => {
7425
7571
  const trimmed = line.trim();
@@ -7470,9 +7616,9 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
7470
7616
  }
7471
7617
  indicators.push("composer.json: symfony/framework-bundle");
7472
7618
  const version = detectSymfonyVersion(projectRoot);
7473
- const hasPublicIndex = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/index.php"));
7474
- const hasServicesConfig = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "config/services.yaml")) || (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "config/services.yml"));
7475
- const hasConfigDir = (0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "config"));
7619
+ const hasPublicIndex = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/index.php"));
7620
+ const hasServicesConfig = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config/services.yaml")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config/services.yml"));
7621
+ const hasConfigDir = (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "config"));
7476
7622
  if (hasPublicIndex) indicators.push("public/index.php");
7477
7623
  if (hasServicesConfig) indicators.push("config/services.yaml");
7478
7624
  if (hasConfigDir) indicators.push("config/");
@@ -7597,15 +7743,15 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
7597
7743
  const warnings = [];
7598
7744
  const suggestions = [];
7599
7745
  const projectRoot = this.config.projectRoot;
7600
- if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/manifest.json"))) {
7746
+ if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/manifest.json"))) {
7601
7747
  warnings.push("manifest.json not found in public directory");
7602
7748
  suggestions.push("Generate manifest.json in public directory");
7603
7749
  }
7604
- if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "public/service-worker.js"))) {
7750
+ if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "public/service-worker.js"))) {
7605
7751
  warnings.push("Service worker not found in public directory");
7606
7752
  suggestions.push("Generate service worker in public directory");
7607
7753
  }
7608
- if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(projectRoot, "templates/offline.html.twig"))) {
7754
+ if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "templates/offline.html.twig"))) {
7609
7755
  warnings.push("Offline fallback template not found");
7610
7756
  suggestions.push(
7611
7757
  "Create templates/offline.html.twig for offline experience"
@@ -7623,11 +7769,11 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
7623
7769
  return env.APP_NAME;
7624
7770
  }
7625
7771
  try {
7626
- const composerPath = (0, import_node_path8.join)(this.config.projectRoot, "composer.json");
7627
- if (!(0, import_node_fs8.existsSync)(composerPath)) {
7772
+ const composerPath = (0, import_node_path9.join)(this.config.projectRoot, "composer.json");
7773
+ if (!(0, import_node_fs9.existsSync)(composerPath)) {
7628
7774
  return "Symfony App";
7629
7775
  }
7630
- const content = (0, import_node_fs8.readFileSync)(composerPath, "utf-8");
7776
+ const content = (0, import_node_fs9.readFileSync)(composerPath, "utf-8");
7631
7777
  const composer = JSON.parse(content);
7632
7778
  const name = typeof composer.name === "string" ? composer.name : "";
7633
7779
  if (!name) {
@@ -7643,16 +7789,28 @@ var SymfonyIntegration = class extends BaseBackendIntegration {
7643
7789
  };
7644
7790
 
7645
7791
  // src/backends/django.ts
7646
- var import_node_fs9 = require("fs");
7647
- var import_node_path9 = require("path");
7648
- function detectDjangoVersion(projectRoot) {
7792
+ var import_node_fs11 = require("fs");
7793
+ var import_node_path11 = require("path");
7794
+
7795
+ // src/backends/python-deps.ts
7796
+ var import_node_fs10 = require("fs");
7797
+ var import_node_path10 = require("path");
7798
+ function getPythonPackageVersion(projectRoot, packageName) {
7799
+ const reqName = packageName.replace(/-/g, "[-_]");
7800
+ const reqRegex = new RegExp(
7801
+ `${reqName}[>=<~!]*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?`,
7802
+ "i"
7803
+ );
7804
+ const pyprojectName = packageName.toLowerCase().replace(/-/g, "_");
7805
+ const pyprojectRegex = new RegExp(
7806
+ `${pyprojectName}\\s*=\\s*["']?[>=<~!]*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?`,
7807
+ "i"
7808
+ );
7649
7809
  try {
7650
- const requirementsPath = (0, import_node_path9.join)(projectRoot, "requirements.txt");
7651
- if ((0, import_node_fs9.existsSync)(requirementsPath)) {
7652
- const content = (0, import_node_fs9.readFileSync)(requirementsPath, "utf-8");
7653
- const match = content.match(
7654
- /Django[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
7655
- );
7810
+ const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
7811
+ if ((0, import_node_fs10.existsSync)(requirementsPath)) {
7812
+ const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
7813
+ const match = content.match(reqRegex);
7656
7814
  if (match) {
7657
7815
  const major = match[1] ?? "0";
7658
7816
  const minor = match[2] ?? "0";
@@ -7663,12 +7821,10 @@ function detectDjangoVersion(projectRoot) {
7663
7821
  } catch {
7664
7822
  }
7665
7823
  try {
7666
- const pyprojectPath = (0, import_node_path9.join)(projectRoot, "pyproject.toml");
7667
- if ((0, import_node_fs9.existsSync)(pyprojectPath)) {
7668
- const content = (0, import_node_fs9.readFileSync)(pyprojectPath, "utf-8");
7669
- const match = content.match(
7670
- /django\s*=\s*["']?[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
7671
- );
7824
+ const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
7825
+ if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
7826
+ const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
7827
+ const match = content.match(pyprojectRegex);
7672
7828
  if (match) {
7673
7829
  const major = match[1] ?? "0";
7674
7830
  const minor = match[2] ?? "0";
@@ -7680,45 +7836,48 @@ function detectDjangoVersion(projectRoot) {
7680
7836
  }
7681
7837
  return null;
7682
7838
  }
7683
- function hasDjangoDependency(projectRoot) {
7839
+ function hasPythonPackage(projectRoot, packageName) {
7684
7840
  try {
7685
- const requirementsPath = (0, import_node_path9.join)(projectRoot, "requirements.txt");
7686
- if ((0, import_node_fs9.existsSync)(requirementsPath)) {
7687
- const content = (0, import_node_fs9.readFileSync)(requirementsPath, "utf-8");
7688
- if (/Django/i.test(content)) {
7841
+ const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
7842
+ if ((0, import_node_fs10.existsSync)(requirementsPath)) {
7843
+ const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
7844
+ if (new RegExp(packageName.replace(/-/g, "[-_]"), "i").test(content)) {
7689
7845
  return true;
7690
7846
  }
7691
7847
  }
7692
- const pyprojectPath = (0, import_node_path9.join)(projectRoot, "pyproject.toml");
7693
- if ((0, import_node_fs9.existsSync)(pyprojectPath)) {
7694
- const content = (0, import_node_fs9.readFileSync)(pyprojectPath, "utf-8");
7695
- if (/django/i.test(content)) {
7848
+ const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
7849
+ if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
7850
+ const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
7851
+ if (new RegExp(packageName.toLowerCase().replace(/-/g, "_"), "i").test(
7852
+ content
7853
+ )) {
7696
7854
  return true;
7697
7855
  }
7698
7856
  }
7699
- return false;
7700
7857
  } catch {
7701
- return false;
7702
7858
  }
7859
+ return false;
7703
7860
  }
7861
+
7862
+ // src/backends/django.ts
7704
7863
  function detectASGI(projectRoot) {
7705
7864
  try {
7706
- if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "asgi.py"))) {
7865
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "asgi.py"))) {
7707
7866
  return true;
7708
7867
  }
7709
- const settingsPath = (0, import_node_path9.join)(projectRoot, "settings.py");
7710
- if ((0, import_node_fs9.existsSync)(settingsPath)) {
7711
- const content = (0, import_node_fs9.readFileSync)(settingsPath, "utf-8");
7868
+ const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
7869
+ if ((0, import_node_fs11.existsSync)(settingsPath)) {
7870
+ const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
7712
7871
  if (/ASGI_APPLICATION/i.test(content)) {
7713
7872
  return true;
7714
7873
  }
7715
7874
  }
7716
- const settingsDir = (0, import_node_path9.join)(projectRoot, "settings");
7717
- if ((0, import_node_fs9.existsSync)(settingsDir)) {
7718
- const files = (0, import_node_fs9.readdirSync)(settingsDir);
7875
+ const settingsDir = (0, import_node_path11.join)(projectRoot, "settings");
7876
+ if ((0, import_node_fs11.existsSync)(settingsDir)) {
7877
+ const files = (0, import_node_fs11.readdirSync)(settingsDir);
7719
7878
  for (const file of files) {
7720
7879
  if (file.endsWith(".py")) {
7721
- const content = (0, import_node_fs9.readFileSync)((0, import_node_path9.join)(settingsDir, file), "utf-8");
7880
+ const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(settingsDir, file), "utf-8");
7722
7881
  if (/ASGI_APPLICATION/i.test(content)) {
7723
7882
  return true;
7724
7883
  }
@@ -7731,17 +7890,13 @@ function detectASGI(projectRoot) {
7731
7890
  }
7732
7891
  }
7733
7892
  function detectDjangoRESTFramework(projectRoot) {
7893
+ if (hasPythonPackage(projectRoot, "djangorestframework")) {
7894
+ return true;
7895
+ }
7734
7896
  try {
7735
- const requirementsPath = (0, import_node_path9.join)(projectRoot, "requirements.txt");
7736
- if ((0, import_node_fs9.existsSync)(requirementsPath)) {
7737
- const content = (0, import_node_fs9.readFileSync)(requirementsPath, "utf-8");
7738
- if (/djangorestframework|django-rest-framework/i.test(content)) {
7739
- return true;
7740
- }
7741
- }
7742
- const settingsPath = (0, import_node_path9.join)(projectRoot, "settings.py");
7743
- if ((0, import_node_fs9.existsSync)(settingsPath)) {
7744
- const content = (0, import_node_fs9.readFileSync)(settingsPath, "utf-8");
7897
+ const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
7898
+ if ((0, import_node_fs11.existsSync)(settingsPath)) {
7899
+ const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
7745
7900
  if (/rest_framework/i.test(content)) {
7746
7901
  return true;
7747
7902
  }
@@ -7754,9 +7909,9 @@ function detectDjangoRESTFramework(projectRoot) {
7754
7909
  function extractStaticFilesConfig(projectRoot) {
7755
7910
  const result = {};
7756
7911
  try {
7757
- const settingsPath = (0, import_node_path9.join)(projectRoot, "settings.py");
7758
- if ((0, import_node_fs9.existsSync)(settingsPath)) {
7759
- const content = (0, import_node_fs9.readFileSync)(settingsPath, "utf-8");
7912
+ const settingsPath = (0, import_node_path11.join)(projectRoot, "settings.py");
7913
+ if ((0, import_node_fs11.existsSync)(settingsPath)) {
7914
+ const content = (0, import_node_fs11.readFileSync)(settingsPath, "utf-8");
7760
7915
  const staticUrlMatch = content.match(/STATIC_URL\s*=\s*['"]([^'"]+)['"]/);
7761
7916
  if (staticUrlMatch) {
7762
7917
  result.staticUrl = staticUrlMatch[1];
@@ -7768,12 +7923,12 @@ function extractStaticFilesConfig(projectRoot) {
7768
7923
  result.staticRoot = staticRootMatch[1];
7769
7924
  }
7770
7925
  }
7771
- const settingsDir = (0, import_node_path9.join)(projectRoot, "settings");
7772
- if ((0, import_node_fs9.existsSync)(settingsDir)) {
7773
- const files = (0, import_node_fs9.readdirSync)(settingsDir);
7926
+ const settingsDir = (0, import_node_path11.join)(projectRoot, "settings");
7927
+ if ((0, import_node_fs11.existsSync)(settingsDir)) {
7928
+ const files = (0, import_node_fs11.readdirSync)(settingsDir);
7774
7929
  for (const file of files) {
7775
7930
  if (file.endsWith(".py")) {
7776
- const content = (0, import_node_fs9.readFileSync)((0, import_node_path9.join)(settingsDir, file), "utf-8");
7931
+ const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(settingsDir, file), "utf-8");
7777
7932
  if (!result.staticUrl) {
7778
7933
  const staticUrlMatch = content.match(
7779
7934
  /STATIC_URL\s*=\s*['"]([^'"]+)['"]/
@@ -7824,23 +7979,23 @@ var DjangoIntegration = class extends BaseBackendIntegration {
7824
7979
  const projectRoot = this.config.projectRoot;
7825
7980
  const indicators = [];
7826
7981
  let confidence = "low";
7827
- if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "manage.py"))) {
7982
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "manage.py"))) {
7828
7983
  indicators.push("manage.py");
7829
7984
  confidence = "medium";
7830
7985
  }
7831
- if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "settings.py")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "settings"))) {
7986
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py")) || (0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings"))) {
7832
7987
  indicators.push("settings.py or settings/");
7833
7988
  if (confidence === "medium") {
7834
7989
  confidence = "high";
7835
7990
  }
7836
7991
  }
7837
- if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "urls.py"))) {
7992
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "urls.py"))) {
7838
7993
  indicators.push("urls.py");
7839
7994
  if (confidence === "medium") {
7840
7995
  confidence = "high";
7841
7996
  }
7842
7997
  }
7843
- if (hasDjangoDependency(projectRoot)) {
7998
+ if (hasPythonPackage(projectRoot, "Django")) {
7844
7999
  indicators.push(
7845
8000
  "Django dependency in requirements.txt or pyproject.toml"
7846
8001
  );
@@ -7848,7 +8003,7 @@ var DjangoIntegration = class extends BaseBackendIntegration {
7848
8003
  confidence = "high";
7849
8004
  }
7850
8005
  }
7851
- const version = detectDjangoVersion(projectRoot);
8006
+ const version = getPythonPackageVersion(projectRoot, "Django");
7852
8007
  return {
7853
8008
  detected: indicators.length > 0,
7854
8009
  framework: "django",
@@ -7996,12 +8151,12 @@ var DjangoIntegration = class extends BaseBackendIntegration {
7996
8151
  const warnings = [];
7997
8152
  const suggestions = [];
7998
8153
  const projectRoot = this.config.projectRoot;
7999
- if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "settings.py")) && !(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "settings"))) {
8154
+ if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py")) && !(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings"))) {
8000
8155
  errors.push("Django settings.py or settings/ directory not found");
8001
8156
  }
8002
- if ((0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "settings.py"))) {
8157
+ if ((0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "settings.py"))) {
8003
8158
  try {
8004
- const content = (0, import_node_fs9.readFileSync)((0, import_node_path9.join)(projectRoot, "settings.py"), "utf-8");
8159
+ const content = (0, import_node_fs11.readFileSync)((0, import_node_path11.join)(projectRoot, "settings.py"), "utf-8");
8005
8160
  if (!/STATIC_URL/i.test(content)) {
8006
8161
  warnings.push("STATIC_URL not found in settings.py");
8007
8162
  }
@@ -8013,10 +8168,10 @@ var DjangoIntegration = class extends BaseBackendIntegration {
8013
8168
  } catch {
8014
8169
  }
8015
8170
  }
8016
- if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "urls.py"))) {
8171
+ if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "urls.py"))) {
8017
8172
  warnings.push("urls.py not found in project root");
8018
8173
  }
8019
- if (!(0, import_node_fs9.existsSync)((0, import_node_path9.join)(projectRoot, "manage.py"))) {
8174
+ if (!(0, import_node_fs11.existsSync)((0, import_node_path11.join)(projectRoot, "manage.py"))) {
8020
8175
  errors.push("manage.py not found (required for Django projects)");
8021
8176
  }
8022
8177
  if (!this.config.isASGI) {
@@ -8063,78 +8218,22 @@ PWA_SW_PATH = STATIC_ROOT / 'sw.js'`,
8063
8218
  };
8064
8219
 
8065
8220
  // src/backends/flask.ts
8066
- var import_node_fs10 = require("fs");
8067
- var import_node_path10 = require("path");
8068
- function detectFlaskVersion(projectRoot) {
8069
- try {
8070
- const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
8071
- if ((0, import_node_fs10.existsSync)(requirementsPath)) {
8072
- const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
8073
- const match = content.match(
8074
- /Flask[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
8075
- );
8076
- if (match) {
8077
- const major = match[1] ?? "0";
8078
- const minor = match[2] ?? "0";
8079
- const patch = match[3] ?? "0";
8080
- return `${major}.${minor}.${patch}`;
8081
- }
8082
- }
8083
- } catch {
8084
- }
8085
- try {
8086
- const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
8087
- if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
8088
- const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
8089
- const match = content.match(
8090
- /flask\s*=\s*["']?[>=<~!]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/i
8091
- );
8092
- if (match) {
8093
- const major = match[1] ?? "0";
8094
- const minor = match[2] ?? "0";
8095
- const patch = match[3] ?? "0";
8096
- return `${major}.${minor}.${patch}`;
8097
- }
8098
- }
8099
- } catch {
8100
- }
8101
- return null;
8102
- }
8103
- function hasFlaskDependency(projectRoot) {
8104
- try {
8105
- const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
8106
- if ((0, import_node_fs10.existsSync)(requirementsPath)) {
8107
- const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
8108
- if (/Flask/i.test(content)) {
8109
- return true;
8110
- }
8111
- }
8112
- const pyprojectPath = (0, import_node_path10.join)(projectRoot, "pyproject.toml");
8113
- if ((0, import_node_fs10.existsSync)(pyprojectPath)) {
8114
- const content = (0, import_node_fs10.readFileSync)(pyprojectPath, "utf-8");
8115
- if (/flask/i.test(content)) {
8116
- return true;
8117
- }
8118
- }
8119
- return false;
8120
- } catch {
8121
- return false;
8122
- }
8123
- }
8221
+ var import_node_fs12 = require("fs");
8222
+ var import_node_path12 = require("path");
8124
8223
  function detectFlaskWTF(projectRoot) {
8125
8224
  try {
8126
- const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
8127
- if ((0, import_node_fs10.existsSync)(requirementsPath)) {
8128
- const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
8225
+ const requirementsPath = (0, import_node_path12.join)(projectRoot, "requirements.txt");
8226
+ if ((0, import_node_fs12.existsSync)(requirementsPath)) {
8227
+ const content = (0, import_node_fs12.readFileSync)(requirementsPath, "utf-8");
8129
8228
  if (/Flask-WTF|flask-wtf/i.test(content)) {
8130
8229
  return true;
8131
8230
  }
8132
8231
  }
8133
8232
  const appFiles = ["app.py", "application.py"];
8134
8233
  for (const file of appFiles) {
8135
- const appPath = (0, import_node_path10.join)(projectRoot, file);
8136
- if ((0, import_node_fs10.existsSync)(appPath)) {
8137
- const content = (0, import_node_fs10.readFileSync)(appPath, "utf-8");
8234
+ const appPath = (0, import_node_path12.join)(projectRoot, file);
8235
+ if ((0, import_node_fs12.existsSync)(appPath)) {
8236
+ const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
8138
8237
  if (/from flask_wtf|import FlaskForm|CSRFProtect/i.test(content)) {
8139
8238
  return true;
8140
8239
  }
@@ -8147,18 +8246,18 @@ function detectFlaskWTF(projectRoot) {
8147
8246
  }
8148
8247
  function detectFlaskRESTful(projectRoot) {
8149
8248
  try {
8150
- const requirementsPath = (0, import_node_path10.join)(projectRoot, "requirements.txt");
8151
- if ((0, import_node_fs10.existsSync)(requirementsPath)) {
8152
- const content = (0, import_node_fs10.readFileSync)(requirementsPath, "utf-8");
8249
+ const requirementsPath = (0, import_node_path12.join)(projectRoot, "requirements.txt");
8250
+ if ((0, import_node_fs12.existsSync)(requirementsPath)) {
8251
+ const content = (0, import_node_fs12.readFileSync)(requirementsPath, "utf-8");
8153
8252
  if (/Flask-RESTful|flask-restful/i.test(content)) {
8154
8253
  return true;
8155
8254
  }
8156
8255
  }
8157
8256
  const appFiles = ["app.py", "application.py"];
8158
8257
  for (const file of appFiles) {
8159
- const appPath = (0, import_node_path10.join)(projectRoot, file);
8160
- if ((0, import_node_fs10.existsSync)(appPath)) {
8161
- const content = (0, import_node_fs10.readFileSync)(appPath, "utf-8");
8258
+ const appPath = (0, import_node_path12.join)(projectRoot, file);
8259
+ if ((0, import_node_fs12.existsSync)(appPath)) {
8260
+ const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
8162
8261
  if (/from flask_restful|import Api|import Resource/i.test(content)) {
8163
8262
  return true;
8164
8263
  }
@@ -8169,14 +8268,71 @@ function detectFlaskRESTful(projectRoot) {
8169
8268
  return false;
8170
8269
  }
8171
8270
  }
8271
+ function detectBlueprints(projectRoot) {
8272
+ const blueprints = [];
8273
+ try {
8274
+ const potentialBlueprintDirs = [
8275
+ "blueprints",
8276
+ "routes",
8277
+ "apps",
8278
+ "modules",
8279
+ "features"
8280
+ ];
8281
+ for (const dir of potentialBlueprintDirs) {
8282
+ const blueprintPath = (0, import_node_path12.join)(projectRoot, dir);
8283
+ if ((0, import_node_fs12.existsSync)(blueprintPath)) {
8284
+ try {
8285
+ const entries = (0, import_node_fs12.readdirSync)(blueprintPath, {
8286
+ withFileTypes: true
8287
+ });
8288
+ for (const entry of entries) {
8289
+ if (entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) {
8290
+ const blueprintFile = (0, import_node_path12.join)(
8291
+ blueprintPath,
8292
+ entry.name,
8293
+ "__init__.py"
8294
+ );
8295
+ if ((0, import_node_fs12.existsSync)(blueprintFile)) {
8296
+ const content = (0, import_node_fs12.readFileSync)(blueprintFile, "utf-8");
8297
+ const blueprintNameMatch = content.match(
8298
+ /Blueprint\s*\(\s*['"]([^'"]+)['"]/
8299
+ );
8300
+ const blueprintName = blueprintNameMatch?.[1] || entry.name;
8301
+ const urlPrefixMatch = content.match(
8302
+ /url_prefix\s*=\s*['"]([^'"]+)['"]/
8303
+ );
8304
+ const prefix = urlPrefixMatch?.[1];
8305
+ const routeMatches = content.match(
8306
+ /@bp\.route\s*\(\s*['"]([^'"]+)['"]/g
8307
+ );
8308
+ const routes = routeMatches?.map(
8309
+ (route) => route.match(/['"]([^'"]+)['"]/)?.[1] || ""
8310
+ ) || [];
8311
+ blueprints.push({
8312
+ name: blueprintName,
8313
+ prefix,
8314
+ folder: (0, import_node_path12.join)(dir, entry.name),
8315
+ routes
8316
+ });
8317
+ }
8318
+ }
8319
+ }
8320
+ } catch {
8321
+ }
8322
+ }
8323
+ }
8324
+ } catch {
8325
+ }
8326
+ return blueprints;
8327
+ }
8172
8328
  function extractStaticFilesConfig2(projectRoot) {
8173
8329
  const result = {};
8174
8330
  try {
8175
8331
  const appFiles = ["app.py", "application.py"];
8176
8332
  for (const file of appFiles) {
8177
- const appPath = (0, import_node_path10.join)(projectRoot, file);
8178
- if ((0, import_node_fs10.existsSync)(appPath)) {
8179
- const content = (0, import_node_fs10.readFileSync)(appPath, "utf-8");
8333
+ const appPath = (0, import_node_path12.join)(projectRoot, file);
8334
+ if ((0, import_node_fs12.existsSync)(appPath)) {
8335
+ const content = (0, import_node_fs12.readFileSync)(appPath, "utf-8");
8180
8336
  const staticFolderMatch = content.match(
8181
8337
  /static_folder\s*=\s*['"]([^'"]+)['"]/
8182
8338
  );
@@ -8204,12 +8360,14 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8204
8360
  constructor(projectRoot, config) {
8205
8361
  super();
8206
8362
  const staticConfig = extractStaticFilesConfig2(projectRoot);
8363
+ const blueprints = detectBlueprints(projectRoot);
8207
8364
  this.config = {
8208
8365
  projectRoot,
8209
8366
  hasFlaskWTF: detectFlaskWTF(projectRoot),
8210
8367
  hasFlaskRESTful: detectFlaskRESTful(projectRoot),
8211
8368
  staticUrl: staticConfig.staticUrl,
8212
8369
  staticFolder: staticConfig.staticFolder,
8370
+ blueprints,
8213
8371
  ...config
8214
8372
  };
8215
8373
  }
@@ -8222,23 +8380,31 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8222
8380
  const projectRoot = this.config.projectRoot;
8223
8381
  const indicators = [];
8224
8382
  let confidence = "low";
8225
- if ((0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "app.py")) || (0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "application.py"))) {
8383
+ if ((0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "app.py")) || (0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "application.py"))) {
8226
8384
  indicators.push("app.py or application.py");
8227
8385
  confidence = "medium";
8228
8386
  }
8229
- if (hasFlaskDependency(projectRoot)) {
8387
+ if (hasPythonPackage(projectRoot, "Flask")) {
8230
8388
  indicators.push("Flask dependency in requirements.txt or pyproject.toml");
8231
8389
  if (confidence === "medium") {
8232
8390
  confidence = "high";
8233
8391
  }
8234
8392
  }
8235
- if ((0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "templates")) || (0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "static"))) {
8393
+ if ((0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "templates")) || (0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "static"))) {
8236
8394
  indicators.push("Flask structure (templates/ or static/)");
8237
8395
  if (confidence === "medium") {
8238
8396
  confidence = "high";
8239
8397
  }
8240
8398
  }
8241
- const version = detectFlaskVersion(projectRoot);
8399
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8400
+ indicators.push(
8401
+ `Flask blueprints (${this.config.blueprints.length} detected)`
8402
+ );
8403
+ if (confidence < "high") {
8404
+ confidence = "high";
8405
+ }
8406
+ }
8407
+ const version = getPythonPackageVersion(projectRoot, "Flask");
8242
8408
  return {
8243
8409
  detected: indicators.length > 0,
8244
8410
  framework: "flask",
@@ -8285,6 +8451,26 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8285
8451
  description: "Flask API endpoints"
8286
8452
  }
8287
8453
  ];
8454
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8455
+ for (const blueprint of this.config.blueprints) {
8456
+ const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
8457
+ apiRoutes.push({
8458
+ pattern: `${prefix}/**`,
8459
+ strategy: {
8460
+ name: "NetworkFirst",
8461
+ cacheName: `flask-blueprint-${blueprint.name}`,
8462
+ networkTimeoutSeconds: 2,
8463
+ expiration: {
8464
+ maxEntries: 30,
8465
+ maxAgeSeconds: 600
8466
+ // 10 minutes
8467
+ }
8468
+ },
8469
+ priority: 15,
8470
+ description: `Flask blueprint: ${blueprint.name}`
8471
+ });
8472
+ }
8473
+ }
8288
8474
  const imageRoutes = [
8289
8475
  {
8290
8476
  pattern: "*.{png,jpg,jpeg,svg,webp,gif}",
@@ -8346,13 +8532,33 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8346
8532
  if (this.config.hasFlaskWTF) {
8347
8533
  routes.push("/csrf-token/**");
8348
8534
  }
8535
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8536
+ for (const blueprint of this.config.blueprints) {
8537
+ if (blueprint.name.toLowerCase().includes("admin") || blueprint.name.toLowerCase().includes("auth")) {
8538
+ const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
8539
+ routes.push(`${prefix}/**`);
8540
+ }
8541
+ }
8542
+ }
8349
8543
  return routes;
8350
8544
  }
8545
+ /**
8546
+ * Get detected blueprints
8547
+ */
8548
+ getBlueprints() {
8549
+ return this.config.blueprints || [];
8550
+ }
8351
8551
  /**
8352
8552
  * Get API endpoint patterns
8353
8553
  */
8354
8554
  getApiPatterns() {
8355
8555
  const patterns = ["/api/**"];
8556
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8557
+ for (const blueprint of this.config.blueprints) {
8558
+ const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
8559
+ patterns.push(`${prefix}/**`);
8560
+ }
8561
+ }
8356
8562
  if (this.config.hasFlaskRESTful) {
8357
8563
  patterns.push("/api/v1/**", "/api/v2/**");
8358
8564
  }
@@ -8379,13 +8585,13 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8379
8585
  const warnings = [];
8380
8586
  const suggestions = [];
8381
8587
  const projectRoot = this.config.projectRoot;
8382
- if (!(0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "app.py")) && !(0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "application.py"))) {
8588
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "app.py")) && !(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "application.py"))) {
8383
8589
  warnings.push("app.py or application.py not found");
8384
8590
  }
8385
- if (!(0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "static"))) {
8591
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "static"))) {
8386
8592
  suggestions.push("Consider creating a static/ folder for static assets");
8387
8593
  }
8388
- if (!(0, import_node_fs10.existsSync)((0, import_node_path10.join)(projectRoot, "templates"))) {
8594
+ if (!(0, import_node_fs12.existsSync)((0, import_node_path12.join)(projectRoot, "templates"))) {
8389
8595
  suggestions.push(
8390
8596
  "Consider creating a templates/ folder for Jinja2 templates"
8391
8597
  );
@@ -8406,8 +8612,7 @@ var FlaskIntegration = class extends BaseBackendIntegration {
8406
8612
  * Inject middleware code for Flask
8407
8613
  */
8408
8614
  injectMiddleware() {
8409
- return {
8410
- code: `# Add PWA support to Flask app
8615
+ let code = `# Add PWA support to Flask app
8411
8616
  from flask import Flask, send_from_directory
8412
8617
 
8413
8618
  app = Flask(__name__)
@@ -8419,19 +8624,42 @@ def manifest():
8419
8624
 
8420
8625
  @app.route('/sw.js')
8421
8626
  def service_worker():
8422
- return send_from_directory('static', 'sw.js', mimetype='application/javascript')
8627
+ return send_from_directory('static', 'sw.js', mimetype='application/javascript')`;
8628
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8629
+ code += "\n\n# Register Flask blueprints\n";
8630
+ for (const blueprint of this.config.blueprints) {
8631
+ const blueprintModule = blueprint.folder.replace(/\//g, ".");
8632
+ code += `from ${blueprintModule} import bp as ${blueprint.name}_bp
8633
+ `;
8634
+ }
8635
+ code += "\n";
8636
+ for (const blueprint of this.config.blueprints) {
8637
+ const prefix = blueprint.prefix || `/${blueprint.name.toLowerCase()}`;
8638
+ code += `app.register_blueprint(${blueprint.name}_bp, url_prefix='${prefix}')
8639
+ `;
8640
+ }
8641
+ }
8642
+ code += `
8423
8643
 
8424
8644
  # Ensure HTTPS in production
8425
8645
  if __name__ == '__main__':
8426
- app.run(ssl_context='adhoc' if app.debug else None)`,
8646
+ app.run(ssl_context='adhoc' if app.debug else None)`;
8647
+ const instructions = [
8648
+ "Add PWA routes to your Flask app",
8649
+ "Place manifest.json and sw.js in the static/ folder",
8650
+ "Ensure HTTPS is enabled in production",
8651
+ "Consider using Flask-WTF for CSRF protection"
8652
+ ];
8653
+ if (this.config.blueprints && this.config.blueprints.length > 0) {
8654
+ instructions.push(
8655
+ `Register ${this.config.blueprints.length} Blueprint(s) to enable optimized caching`
8656
+ );
8657
+ }
8658
+ return {
8659
+ code,
8427
8660
  path: "app.py",
8428
8661
  language: "python",
8429
- instructions: [
8430
- "Add PWA routes to your Flask app",
8431
- "Place manifest.json and sw.js in the static/ folder",
8432
- "Ensure HTTPS is enabled in production",
8433
- "Consider using Flask-WTF for CSRF protection"
8434
- ]
8662
+ instructions
8435
8663
  };
8436
8664
  }
8437
8665
  };
@@ -8516,9 +8744,11 @@ function resetBackendFactory() {
8516
8744
  ConfigLoadError,
8517
8745
  ConfigValidationError,
8518
8746
  DEFAULT_CONFIG,
8747
+ DEFAULT_INJECTION_EXTENSIONS,
8519
8748
  DefaultBackendIntegrationFactory,
8520
8749
  DjangoIntegration,
8521
8750
  FlaskIntegration,
8751
+ INJECTION_EXTENSIONS_BY_PROJECT_TYPE,
8522
8752
  IconConfigSchema,
8523
8753
  InjectionConfigSchema,
8524
8754
  LaravelIntegration,
@@ -8534,6 +8764,7 @@ function resetBackendFactory() {
8534
8764
  SymfonyIntegration,
8535
8765
  TelemetryCollector,
8536
8766
  UniversalPWAConfigSchema,
8767
+ WORDPRESS_INJECTION_PATTERNS,
8537
8768
  buildDependencyGraph,
8538
8769
  checkHttps,
8539
8770
  checkProjectHttps,
@@ -8590,6 +8821,7 @@ function resetBackendFactory() {
8590
8821
  injectMetaTagsInFile,
8591
8822
  injectMetaTagsInFilesBatch,
8592
8823
  loadConfig,
8824
+ mapBackendManifestVarsToOptions,
8593
8825
  optimizeImage,
8594
8826
  optimizeProject,
8595
8827
  optimizeProjectImages,