@gzl10/nexus-backend 0.19.0 → 0.20.0

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/cli.js CHANGED
@@ -865,13 +865,17 @@ import { readFileSync as readFileSync4 } from "fs";
865
865
  import { dirname as dirname5, join as join6 } from "path";
866
866
  import { fileURLToPath as fileURLToPath2 } from "url";
867
867
  function getVersion() {
868
- const __dir = dirname5(fileURLToPath2(import.meta.url));
869
- try {
870
- const pkg3 = JSON.parse(readFileSync4(join6(__dir, "..", "..", "package.json"), "utf-8"));
871
- return pkg3.version;
872
- } catch {
873
- return "unknown";
868
+ let dir = dirname5(fileURLToPath2(import.meta.url));
869
+ for (let i = 0; i < 5; i++) {
870
+ const candidate = join6(dir, "package.json");
871
+ try {
872
+ const pkg3 = JSON.parse(readFileSync4(candidate, "utf-8"));
873
+ if (pkg3.name === "@gzl10/nexus-backend") return pkg3.version;
874
+ } catch {
875
+ }
876
+ dir = dirname5(dir);
874
877
  }
878
+ return "unknown";
875
879
  }
876
880
  async function collectInfo() {
877
881
  const config3 = resolveConfig();
@@ -8705,18 +8709,20 @@ function createSystemController(ctx) {
8705
8709
  /**
8706
8710
  * GET /system/capabilities
8707
8711
  * Public endpoint — no authentication required.
8708
- * Returns backend version and registered plugin codes.
8712
+ * Resolves all registered capabilities (core + plugin-contributed).
8709
8713
  */
8710
- getCapabilities(_req, res) {
8711
- res.set("Cache-Control", "public, max-age=300");
8712
- const manifest = engine.getCoreManifest();
8713
- const plugins = engine.getPlugins();
8714
- const body = {
8715
- version: manifest.version,
8716
- plugins: plugins.map((p) => p.code),
8717
- locales: ctx.locales
8718
- };
8719
- res.json(body);
8714
+ async getCapabilities(_req, res) {
8715
+ try {
8716
+ res.set("Cache-Control", "public, max-age=300");
8717
+ const body = await ctx.capabilities.resolve();
8718
+ res.json(body);
8719
+ } catch (err) {
8720
+ ctx.core.logger.error({ err }, "Failed to resolve capabilities");
8721
+ res.set("Cache-Control", "no-cache");
8722
+ res.status(500).json({
8723
+ error: { code: "INTERNAL_ERROR", message: "Failed to resolve capabilities" }
8724
+ });
8725
+ }
8720
8726
  }
8721
8727
  };
8722
8728
  function toManifestDTO(manifest, req) {
@@ -9289,8 +9295,8 @@ function registerCoreVars(registry2) {
9289
9295
  registry2.register("auth", "core", [
9290
9296
  {
9291
9297
  name: "AUTH_SECRET",
9292
- description: { en: "JWT signing secret. Must be at least 32 characters. Use a cryptographically random string in production", es: "Clave de firma JWT. M\xEDnimo 32 caracteres. Usar una cadena criptogr\xE1ficamente aleatoria en producci\xF3n" },
9293
- required: true,
9298
+ description: { en: "JWT signing secret (optional). If not set, auto-generated and persisted in database. Must be at least 32 characters. Set explicitly for multi-instance deployments", es: "Clave de firma JWT (opcional). Si no se establece, se genera autom\xE1ticamente y se persiste en la base de datos. M\xEDnimo 32 caracteres. Establecer expl\xEDcitamente para despliegues multi-instancia" },
9299
+ required: false,
9294
9300
  sensitive: true
9295
9301
  },
9296
9302
  {
@@ -9807,96 +9813,70 @@ var init_system = __esm({
9807
9813
  }
9808
9814
  });
9809
9815
 
9810
- // src/modules/ui-settings/ui-branding.entity.ts
9811
- import { useTextField as useTextField7, useImageField } from "@gzl10/nexus-sdk/fields";
9812
- var uiBrandingEntity;
9813
- var init_ui_branding_entity = __esm({
9814
- "src/modules/ui-settings/ui-branding.entity.ts"() {
9816
+ // src/modules/ui-settings/ui-style-guide.entity.ts
9817
+ import { useTextField as useTextField7, useImageField, useSelectField as useSelectField6, useColorField, useNumberField as useNumberField4, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
9818
+ var uiStyleGuideEntity;
9819
+ var init_ui_style_guide_entity = __esm({
9820
+ "src/modules/ui-settings/ui-style-guide.entity.ts"() {
9815
9821
  "use strict";
9816
- uiBrandingEntity = {
9822
+ uiStyleGuideEntity = {
9817
9823
  type: "single",
9818
9824
  realtime: "sync",
9819
- key: "ui_branding",
9820
- label: { en: "Branding", es: "Identidad Corporativa" },
9821
- icon: "mdi:palette-swatch-outline",
9825
+ key: "ui_style_guide",
9826
+ label: { en: "Style Guide", es: "Gu\xEDa de Estilos" },
9827
+ icon: "mdi:palette-outline",
9822
9828
  public: true,
9823
- routePrefix: "/ui-branding",
9829
+ routePrefix: "/ui-style-guide",
9824
9830
  defaults: {
9825
9831
  appName: "Nexus",
9826
9832
  logo: null,
9827
9833
  logoDark: null,
9828
- favicon: null
9834
+ favicon: null,
9835
+ primaryColor: "#3B82F6",
9836
+ font: "space-grotesk",
9837
+ typographyScale: "default",
9838
+ borderRadius: 8,
9839
+ glassEffect: true,
9840
+ theme: "system",
9841
+ loginLayout: "centered"
9829
9842
  },
9830
9843
  fields: {
9831
9844
  appName: useTextField7({
9832
9845
  label: { en: "App Name", es: "Nombre de la App" },
9833
- hint: { en: "Displayed in the header, browser tab and emails", es: "Se muestra en el header, pesta\xF1a del navegador y emails" },
9846
+ hint: { en: "Displayed in header, browser tab and emails", es: "Se muestra en header, pesta\xF1a del navegador y emails" },
9834
9847
  size: 100,
9835
9848
  required: true
9836
9849
  }),
9837
9850
  logo: useImageField({
9838
9851
  label: { en: "Logo (Light Theme)", es: "Logo (Tema Claro)" },
9839
- hint: { en: "Recommended: SVG or PNG with transparent background, max 200px height", es: "Recomendado: SVG o PNG con fondo transparente, m\xE1x 200px de alto" },
9852
+ hint: { en: "SVG or PNG with transparent background, max 200px height", es: "SVG o PNG con fondo transparente, m\xE1x 200px de alto" },
9840
9853
  folder: "branding",
9841
9854
  isPublic: true,
9842
9855
  dedupe: true
9843
9856
  }),
9844
9857
  logoDark: useImageField({
9845
9858
  label: { en: "Logo (Dark Theme)", es: "Logo (Tema Oscuro)" },
9846
- hint: { en: "Optional: Use a lighter version for dark backgrounds", es: "Opcional: Usa una versi\xF3n m\xE1s clara para fondos oscuros" },
9859
+ hint: { en: "Lighter version for dark backgrounds", es: "Versi\xF3n m\xE1s clara para fondos oscuros" },
9847
9860
  folder: "branding",
9848
9861
  isPublic: true,
9849
9862
  dedupe: true
9850
9863
  }),
9851
9864
  favicon: useImageField({
9852
9865
  label: { en: "Favicon", es: "Favicon" },
9853
- hint: { en: "Browser tab icon. Recommended: 32x32 or 64x64 PNG/ICO", es: "Icono de pesta\xF1a del navegador. Recomendado: 32x32 o 64x64 PNG/ICO" },
9866
+ hint: { en: "32x32 or 64x64 PNG/ICO", es: "32x32 o 64x64 PNG/ICO" },
9854
9867
  accept: "image/x-icon,image/png,image/svg+xml",
9855
9868
  maxSize: "256KB",
9856
9869
  folder: "branding",
9857
9870
  isPublic: true,
9858
9871
  dedupe: true
9859
- })
9860
- },
9861
- casl: {
9862
- subject: "UiBranding",
9863
- permissions: {
9864
- ADMIN: { actions: ["read", "update"] }
9865
- }
9866
- }
9867
- };
9868
- }
9869
- });
9870
-
9871
- // src/modules/ui-settings/ui-theme.entity.ts
9872
- import { useSelectField as useSelectField6, useColorField } from "@gzl10/nexus-sdk/fields";
9873
- var uiThemeEntity;
9874
- var init_ui_theme_entity = __esm({
9875
- "src/modules/ui-settings/ui-theme.entity.ts"() {
9876
- "use strict";
9877
- uiThemeEntity = {
9878
- type: "single",
9879
- realtime: "sync",
9880
- key: "ui_theme",
9881
- label: { en: "Theme & Colors", es: "Tema y Colores" },
9882
- icon: "mdi:palette",
9883
- public: true,
9884
- routePrefix: "/ui-theme",
9885
- defaults: {
9886
- // Typography
9887
- font: "space-grotesk",
9888
- // Theme & Colors
9889
- theme: "system",
9890
- primaryColor: "#3B82F6",
9891
- dopamineTheme: "none",
9892
- // Layout
9893
- loginLayout: "centered"
9894
- },
9895
- fields: {
9896
- // === Typography ===
9872
+ }),
9873
+ primaryColor: useColorField({
9874
+ label: { en: "Primary Color", es: "Color Principal" },
9875
+ hint: { en: "Accent color for buttons, links and highlights", es: "Color de acento para botones, enlaces y resaltados" }
9876
+ }),
9897
9877
  font: useSelectField6({
9898
9878
  label: { en: "Font", es: "Fuente" },
9899
- hint: { en: "Primary font for headings and UI elements", es: "Fuente principal para t\xEDtulos y elementos de interfaz" },
9879
+ hint: { en: "Primary font for headings and UI", es: "Fuente principal para t\xEDtulos e interfaz" },
9900
9880
  options: [
9901
9881
  { value: "space-grotesk", label: "Space Grotesk" },
9902
9882
  { value: "inter", label: "Inter" },
@@ -9906,37 +9886,38 @@ var init_ui_theme_entity = __esm({
9906
9886
  { value: "system", label: { en: "System Default", es: "Sistema" } }
9907
9887
  ]
9908
9888
  }),
9909
- // === Theme & Colors ===
9889
+ typographyScale: useSelectField6({
9890
+ label: { en: "Typography Scale", es: "Escala Tipogr\xE1fica" },
9891
+ hint: { en: "Font size scaling (WCAG 1.4.4)", es: "Escala de tama\xF1os de fuente (WCAG 1.4.4)" },
9892
+ options: [
9893
+ { value: "compact", label: { en: "Compact", es: "Compacta" } },
9894
+ { value: "default", label: { en: "Default", es: "Por defecto" } },
9895
+ { value: "relaxed", label: { en: "Relaxed", es: "Relajada" } }
9896
+ ]
9897
+ }),
9898
+ borderRadius: useNumberField4({
9899
+ label: { en: "Border Radius", es: "Radio de Bordes" },
9900
+ hint: { en: "Corner roundness in pixels (0 = sharp, 16 = very round)", es: "Redondeo de esquinas en p\xEDxeles (0 = cuadrado, 16 = muy redondo)" },
9901
+ defaultValue: 8,
9902
+ validation: { min: 0, max: 16 }
9903
+ }),
9904
+ glassEffect: useSwitchField2({
9905
+ label: { en: "Glass Effect", es: "Efecto Glass" },
9906
+ hint: { en: "Glassmorphism blur on cards and modals", es: "Desenfoque glassmorphism en cards y modales" },
9907
+ defaultValue: true
9908
+ }),
9910
9909
  theme: useSelectField6({
9911
9910
  label: { en: "Theme", es: "Tema" },
9912
- hint: { en: "System follows your device preferences", es: "Sistema sigue las preferencias de tu dispositivo" },
9911
+ hint: { en: "System follows device preferences", es: "Sistema sigue las preferencias del dispositivo" },
9913
9912
  options: [
9914
9913
  { value: "light", label: { en: "Light", es: "Claro" } },
9915
9914
  { value: "dark", label: { en: "Dark", es: "Oscuro" } },
9916
9915
  { value: "system", label: { en: "System", es: "Sistema" } }
9917
9916
  ]
9918
9917
  }),
9919
- dopamineTheme: useSelectField6({
9920
- label: { en: "Dopamine Theme", es: "Tema Dopamina" },
9921
- hint: { en: "Vibrant color presets optimized for light and dark modes (2025/2026 trends)", es: "Presets de colores vibrantes optimizados para modo claro y oscuro (tendencias 2025/2026)" },
9922
- options: [
9923
- { value: "none", label: { en: "None (Custom Color)", es: "Ninguno (Color personalizado)" } },
9924
- { value: "electric", label: { en: "Electric (Cobalt Blue)", es: "El\xE9ctrico (Azul Cobalto)" } },
9925
- { value: "sunset", label: { en: "Sunset (Coral Red)", es: "Atardecer (Rojo Coral)" } },
9926
- { value: "ocean", label: { en: "Ocean (Teal)", es: "Oc\xE9ano (Verde Azulado)" } },
9927
- { value: "forest", label: { en: "Forest (Mint)", es: "Bosque (Menta)" } },
9928
- { value: "lavender", label: { en: "Lavender (Violet)", es: "Lavanda (Violeta)" } },
9929
- { value: "cherry", label: { en: "Cherry (Fuchsia)", es: "Cereza (Fucsia)" } },
9930
- { value: "amber", label: { en: "Amber (Golden)", es: "\xC1mbar (Dorado)" } },
9931
- { value: "tangerine", label: { en: "Tangerine (Orange)", es: "Mandarina (Naranja)" } },
9932
- { value: "slate", label: { en: "Slate (Cool Gray)", es: "Pizarra (Gris Fr\xEDo)" } },
9933
- { value: "bronze", label: { en: "Bronze (Earth)", es: "Bronce (Tierra)" } }
9934
- ]
9935
- }),
9936
- // === Login Layout ===
9937
9918
  loginLayout: useSelectField6({
9938
9919
  label: { en: "Login Layout", es: "Dise\xF1o de Login" },
9939
- hint: { en: "Visual layout for authentication pages", es: "Dise\xF1o visual para p\xE1ginas de autenticaci\xF3n" },
9920
+ hint: { en: "Auth page visual layout", es: "Dise\xF1o visual de p\xE1ginas de autenticaci\xF3n" },
9940
9921
  options: [
9941
9922
  { value: "centered", label: { en: "Centered", es: "Centrado" } },
9942
9923
  { value: "split", label: { en: "Split", es: "Dividido" } },
@@ -9944,140 +9925,10 @@ var init_ui_theme_entity = __esm({
9944
9925
  { value: "floating", label: { en: "Floating", es: "Flotante" } },
9945
9926
  { value: "minimal", label: { en: "Minimal", es: "Minimalista" } }
9946
9927
  ]
9947
- }),
9948
- primaryColor: {
9949
- ...useColorField({
9950
- label: { en: "Primary Color", es: "Color Principal" },
9951
- hint: { en: "Custom accent color for buttons, links and highlights", es: "Color de acento personalizado para botones, enlaces y resaltados" }
9952
- }),
9953
- // Hide when a dopamine theme is active (show only if 'none')
9954
- hidden: { field: "dopamineTheme", $ne: "none" }
9955
- }
9956
- },
9957
- casl: {
9958
- subject: "UiTheme",
9959
- permissions: {
9960
- ADMIN: { actions: ["read", "update"] }
9961
- }
9962
- }
9963
- };
9964
- }
9965
- });
9966
-
9967
- // src/modules/ui-settings/ui-effects.entity.ts
9968
- import { useSelectField as useSelectField7, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
9969
- var uiEffectsEntity;
9970
- var init_ui_effects_entity = __esm({
9971
- "src/modules/ui-settings/ui-effects.entity.ts"() {
9972
- "use strict";
9973
- uiEffectsEntity = {
9974
- type: "single",
9975
- realtime: "sync",
9976
- key: "ui_effects",
9977
- label: { en: "Visual Effects", es: "Efectos Visuales" },
9978
- icon: "mdi:shimmer",
9979
- public: true,
9980
- routePrefix: "/ui-effects",
9981
- defaults: {
9982
- glassIntensity: "medium",
9983
- borderStyle: "rounded",
9984
- enableAnimations: true,
9985
- enableOrganicShapes: false
9986
- },
9987
- fields: {
9988
- glassIntensity: useSelectField7({
9989
- label: { en: "Glass Intensity", es: "Intensidad Glass" },
9990
- hint: { en: "Glassmorphism blur effect on cards and modals", es: "Efecto de desenfoque glassmorphism en cards y modales" },
9991
- options: [
9992
- { value: "none", label: { en: "None", es: "Ninguno" } },
9993
- { value: "low", label: { en: "Low", es: "Baja" } },
9994
- { value: "medium", label: { en: "Medium", es: "Media" } },
9995
- { value: "high", label: { en: "High", es: "Alta" } }
9996
- ]
9997
- }),
9998
- borderStyle: useSelectField7({
9999
- label: { en: "Border Style", es: "Estilo de Bordes" },
10000
- hint: { en: "Corner radius for buttons, cards and inputs", es: "Radio de esquinas para botones, cards e inputs" },
10001
- options: [
10002
- { value: "sharp", label: { en: "Sharp", es: "Cuadrados" } },
10003
- { value: "rounded", label: { en: "Rounded", es: "Redondeados" } },
10004
- { value: "organic", label: { en: "Organic", es: "Org\xE1nicos" } }
10005
- ]
10006
- }),
10007
- enableAnimations: useSwitchField2({
10008
- label: { en: "Enable Animations", es: "Habilitar Animaciones" },
10009
- hint: { en: "Micro-interactions and transitions (hover, focus, page changes)", es: "Micro-interacciones y transiciones (hover, focus, cambios de p\xE1gina)" },
10010
- defaultValue: true,
10011
- meta: { sortable: true }
10012
- }),
10013
- enableOrganicShapes: useSwitchField2({
10014
- label: { en: "Enable Organic Shapes", es: "Habilitar Formas Org\xE1nicas" },
10015
- hint: { en: "Asymmetric border-radius for a more natural, playful look", es: "Border-radius asim\xE9trico para un aspecto m\xE1s natural y divertido" },
10016
- defaultValue: false,
10017
- meta: { sortable: true },
10018
- hidden: { field: "borderStyle", $ne: "organic" }
10019
- })
10020
- },
10021
- casl: {
10022
- subject: "UiEffects",
10023
- permissions: {
10024
- ADMIN: { actions: ["read", "update"] }
10025
- }
10026
- }
10027
- };
10028
- }
10029
- });
10030
-
10031
- // src/modules/ui-settings/ui-accessibility.entity.ts
10032
- import { useSelectField as useSelectField8, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
10033
- var uiAccessibilityEntity;
10034
- var init_ui_accessibility_entity = __esm({
10035
- "src/modules/ui-settings/ui-accessibility.entity.ts"() {
10036
- "use strict";
10037
- uiAccessibilityEntity = {
10038
- type: "single",
10039
- realtime: "sync",
10040
- key: "ui_accessibility",
10041
- label: { en: "Accessibility", es: "Accesibilidad" },
10042
- icon: "mdi:human",
10043
- public: true,
10044
- routePrefix: "/ui-accessibility",
10045
- defaults: {
10046
- typographyScale: "default",
10047
- reducedMotion: false,
10048
- highContrast: false
10049
- },
10050
- fields: {
10051
- typographyScale: useSelectField8({
10052
- label: { en: "Typography Scale", es: "Escala Tipogr\xE1fica" },
10053
- hint: { en: "Adjusts font sizes for better readability (WCAG 1.4.4)", es: "Ajusta tama\xF1os de fuente para mejor legibilidad (WCAG 1.4.4)" },
10054
- options: [
10055
- { value: "compact", label: { en: "Compact (smaller)", es: "Compacta (m\xE1s peque\xF1a)" } },
10056
- { value: "default", label: { en: "Default", es: "Por defecto" } },
10057
- { value: "relaxed", label: { en: "Relaxed (larger)", es: "Relajada (m\xE1s grande)" } }
10058
- ]
10059
- }),
10060
- reducedMotion: useSwitchField3({
10061
- label: { en: "Reduced Motion", es: "Movimiento Reducido" },
10062
- defaultValue: false,
10063
- meta: { sortable: true },
10064
- hint: {
10065
- en: "Minimizes animations for users sensitive to motion (WCAG 2.1)",
10066
- es: "Minimiza animaciones para usuarios sensibles al movimiento (WCAG 2.1)"
10067
- }
10068
- }),
10069
- highContrast: useSwitchField3({
10070
- label: { en: "High Contrast", es: "Alto Contraste" },
10071
- defaultValue: false,
10072
- meta: { sortable: true },
10073
- hint: {
10074
- en: "Increases text contrast for better readability (WCAG AAA)",
10075
- es: "Aumenta el contraste del texto para mejor legibilidad (WCAG AAA)"
10076
- }
10077
9928
  })
10078
9929
  },
10079
9930
  casl: {
10080
- subject: "UiAccessibility",
9931
+ subject: "UiStyleGuide",
10081
9932
  permissions: {
10082
9933
  ADMIN: { actions: ["read", "update"] }
10083
9934
  }
@@ -10087,44 +9938,62 @@ var init_ui_accessibility_entity = __esm({
10087
9938
  });
10088
9939
 
10089
9940
  // src/modules/ui-settings/index.ts
9941
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
9942
+ import { join as join9 } from "path";
10090
9943
  var uiSettingsModule;
10091
9944
  var init_ui_settings = __esm({
10092
9945
  "src/modules/ui-settings/index.ts"() {
10093
9946
  "use strict";
10094
- init_ui_branding_entity();
10095
- init_ui_theme_entity();
10096
- init_ui_effects_entity();
10097
- init_ui_accessibility_entity();
10098
- init_ui_branding_entity();
10099
- init_ui_theme_entity();
10100
- init_ui_effects_entity();
10101
- init_ui_accessibility_entity();
9947
+ init_ui_style_guide_entity();
9948
+ init_ui_style_guide_entity();
10102
9949
  uiSettingsModule = {
10103
9950
  name: "ui-settings",
10104
- label: { en: "UI Settings", es: "Configuraci\xF3n de UI" },
9951
+ label: { en: "Style Guide", es: "Gu\xEDa de Estilos" },
10105
9952
  icon: "mdi:palette-outline",
10106
9953
  description: {
10107
- en: "User interface configuration: branding, themes, visual effects, and accessibility",
10108
- es: "Configuraci\xF3n de interfaz de usuario: marca, temas, efectos visuales y accesibilidad"
9954
+ en: "Brand identity and visual style configuration",
9955
+ es: "Configuraci\xF3n de identidad de marca y estilo visual"
10109
9956
  },
10110
9957
  type: "core",
10111
9958
  category: "settings",
10112
9959
  dependencies: ["logger"],
10113
- definitions: [
10114
- uiBrandingEntity,
10115
- uiThemeEntity,
10116
- uiEffectsEntity,
10117
- uiAccessibilityEntity
10118
- ],
10119
- routePrefix: "/ui-settings"
9960
+ definitions: [uiStyleGuideEntity],
9961
+ routePrefix: "/ui-settings",
9962
+ /**
9963
+ * Seed style guide from data/seeds/ui-style-guide.json if it exists.
9964
+ * Idempotent: only inserts if no record exists yet.
9965
+ *
9966
+ * Workflow:
9967
+ * 1. Configure style guide in dev via admin UI
9968
+ * 2. Export: GET /api/v1/ui-settings/ui-style-guide → save to data/seeds/ui-style-guide.json
9969
+ * 3. Commit the seed file
9970
+ * 4. On other environments, seed runs automatically on startup
9971
+ */
9972
+ seed: async (ctx) => {
9973
+ const db3 = ctx.db.knex;
9974
+ const existing = await db3("single_records").where("key", "ui_style_guide").first();
9975
+ if (existing) return;
9976
+ const seedPath = join9(process.cwd(), "data", "seeds", "ui-style-guide.json");
9977
+ if (!existsSync6(seedPath)) return;
9978
+ try {
9979
+ const seedData = JSON.parse(readFileSync6(seedPath, "utf-8"));
9980
+ await db3("single_records").insert({
9981
+ key: "ui_style_guide",
9982
+ value: JSON.stringify(seedData)
9983
+ });
9984
+ ctx.core.logger.info("Seeded ui_style_guide from data/seeds/ui-style-guide.json");
9985
+ } catch {
9986
+ ctx.core.logger.warn("Failed to parse data/seeds/ui-style-guide.json, skipping seed");
9987
+ }
9988
+ }
10120
9989
  };
10121
9990
  }
10122
9991
  });
10123
9992
 
10124
9993
  // src/modules/storage/drivers/filesystem.driver.ts
10125
- import { createReadStream, createWriteStream, existsSync as existsSync6, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
9994
+ import { createReadStream, createWriteStream, existsSync as existsSync7, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
10126
9995
  import { readFile, readdir, stat, copyFile, rename, mkdir } from "fs/promises";
10127
- import { join as join9, dirname as dirname6, extname, basename } from "path";
9996
+ import { join as join10, dirname as dirname6, extname, basename } from "path";
10128
9997
  import { createHash } from "crypto";
10129
9998
  var FilesystemDriver;
10130
9999
  var init_filesystem_driver = __esm({
@@ -10138,7 +10007,7 @@ var init_filesystem_driver = __esm({
10138
10007
  this.basePath = config3.basePath;
10139
10008
  this.baseUrl = config3.baseUrl;
10140
10009
  this.generateId = config3.generateId;
10141
- if (!existsSync6(this.basePath)) {
10010
+ if (!existsSync7(this.basePath)) {
10142
10011
  mkdirSync3(this.basePath, { recursive: true });
10143
10012
  }
10144
10013
  }
@@ -10148,9 +10017,9 @@ var init_filesystem_driver = __esm({
10148
10017
  const diskFilename = `${id}${ext}`;
10149
10018
  const folder = options?.folder || "";
10150
10019
  const relativePath = folder ? `${folder}/${diskFilename}` : diskFilename;
10151
- const fullPath = join9(this.basePath, relativePath);
10020
+ const fullPath = join10(this.basePath, relativePath);
10152
10021
  const dir = dirname6(fullPath);
10153
- if (!existsSync6(dir)) {
10022
+ if (!existsSync7(dir)) {
10154
10023
  mkdirSync3(dir, { recursive: true });
10155
10024
  }
10156
10025
  const hash = createHash("sha256").update(buffer).digest("hex");
@@ -10174,25 +10043,25 @@ var init_filesystem_driver = __esm({
10174
10043
  };
10175
10044
  }
10176
10045
  async get(path3) {
10177
- const fullPath = join9(this.basePath, path3);
10178
- if (!existsSync6(fullPath)) {
10046
+ const fullPath = join10(this.basePath, path3);
10047
+ if (!existsSync7(fullPath)) {
10179
10048
  throw new Error(`File not found: ${path3}`);
10180
10049
  }
10181
10050
  return createReadStream(fullPath);
10182
10051
  }
10183
10052
  async getBuffer(path3) {
10184
- const fullPath = join9(this.basePath, path3);
10053
+ const fullPath = join10(this.basePath, path3);
10185
10054
  return readFile(fullPath);
10186
10055
  }
10187
10056
  async delete(path3) {
10188
- const fullPath = join9(this.basePath, path3);
10189
- if (existsSync6(fullPath)) {
10057
+ const fullPath = join10(this.basePath, path3);
10058
+ if (existsSync7(fullPath)) {
10190
10059
  unlinkSync(fullPath);
10191
10060
  }
10192
10061
  }
10193
10062
  async exists(path3) {
10194
- const fullPath = join9(this.basePath, path3);
10195
- return existsSync6(fullPath);
10063
+ const fullPath = join10(this.basePath, path3);
10064
+ return existsSync7(fullPath);
10196
10065
  }
10197
10066
  getUrl(path3) {
10198
10067
  if (!this.baseUrl) return null;
@@ -10202,14 +10071,14 @@ var init_filesystem_driver = __esm({
10202
10071
  // EXTENDED METHODS
10203
10072
  // ============================================================================
10204
10073
  async list(folder) {
10205
- const targetPath = folder ? join9(this.basePath, folder) : this.basePath;
10206
- if (!existsSync6(targetPath)) {
10074
+ const targetPath = folder ? join10(this.basePath, folder) : this.basePath;
10075
+ if (!existsSync7(targetPath)) {
10207
10076
  return [];
10208
10077
  }
10209
10078
  const entries = await readdir(targetPath, { withFileTypes: true });
10210
10079
  const results = [];
10211
10080
  for (const entry of entries) {
10212
- const entryPath = join9(targetPath, entry.name);
10081
+ const entryPath = join10(targetPath, entry.name);
10213
10082
  const relativePath = folder ? `${folder}/${entry.name}` : entry.name;
10214
10083
  const stats = await stat(entryPath);
10215
10084
  results.push({
@@ -10223,10 +10092,10 @@ var init_filesystem_driver = __esm({
10223
10092
  return results;
10224
10093
  }
10225
10094
  async copy(src, dst) {
10226
- const srcPath = join9(this.basePath, src);
10227
- const dstPath = join9(this.basePath, dst);
10095
+ const srcPath = join10(this.basePath, src);
10096
+ const dstPath = join10(this.basePath, dst);
10228
10097
  const dstDir = dirname6(dstPath);
10229
- if (!existsSync6(dstDir)) {
10098
+ if (!existsSync7(dstDir)) {
10230
10099
  await mkdir(dstDir, { recursive: true });
10231
10100
  }
10232
10101
  await copyFile(srcPath, dstPath);
@@ -10249,10 +10118,10 @@ var init_filesystem_driver = __esm({
10249
10118
  };
10250
10119
  }
10251
10120
  async move(src, dst) {
10252
- const srcPath = join9(this.basePath, src);
10253
- const dstPath = join9(this.basePath, dst);
10121
+ const srcPath = join10(this.basePath, src);
10122
+ const dstPath = join10(this.basePath, dst);
10254
10123
  const dstDir = dirname6(dstPath);
10255
- if (!existsSync6(dstDir)) {
10124
+ if (!existsSync7(dstDir)) {
10256
10125
  await mkdir(dstDir, { recursive: true });
10257
10126
  }
10258
10127
  await rename(srcPath, dstPath);
@@ -10275,7 +10144,7 @@ var init_filesystem_driver = __esm({
10275
10144
  };
10276
10145
  }
10277
10146
  async getMetadata(path3) {
10278
- const fullPath = join9(this.basePath, path3);
10147
+ const fullPath = join10(this.basePath, path3);
10279
10148
  const stats = await stat(fullPath);
10280
10149
  return {
10281
10150
  path: path3,
@@ -10289,8 +10158,8 @@ var init_filesystem_driver = __esm({
10289
10158
  throw new Error("Signed URLs are not supported by the filesystem driver");
10290
10159
  }
10291
10160
  async createFolder(path3) {
10292
- const fullPath = join9(this.basePath, path3);
10293
- if (!existsSync6(fullPath)) {
10161
+ const fullPath = join10(this.basePath, path3);
10162
+ if (!existsSync7(fullPath)) {
10294
10163
  await mkdir(fullPath, { recursive: true });
10295
10164
  }
10296
10165
  }
@@ -10521,8 +10390,8 @@ var init_s3_driver = __esm({
10521
10390
  });
10522
10391
 
10523
10392
  // src/modules/storage/storage.config.ts
10524
- import { join as join10, isAbsolute as isAbsolute2 } from "path";
10525
- import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
10393
+ import { join as join11, isAbsolute as isAbsolute2 } from "path";
10394
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
10526
10395
  function getDefaultScope(driver) {
10527
10396
  return driver === "s3" ? DEFAULT_S3_SCOPE : DEFAULT_FILESYSTEM_SCOPE;
10528
10397
  }
@@ -10537,7 +10406,7 @@ function resolveStoragePath(path3, projPath) {
10537
10406
  return path3;
10538
10407
  }
10539
10408
  const cleanPath = path3.startsWith("./") ? path3.slice(2) : path3;
10540
- return join10(projPath, "data", cleanPath);
10409
+ return join11(projPath, "data", cleanPath);
10541
10410
  }
10542
10411
  async function getConfigByScope(db3, scope) {
10543
10412
  if (!generateIdFn) {
@@ -10568,7 +10437,7 @@ function buildConfigFromRow(row) {
10568
10437
  if (row.driver === "filesystem") {
10569
10438
  const fsMeta = metadata;
10570
10439
  config3.basePath = resolveStoragePath(fsMeta.basePath || "./storage", projectPath);
10571
- if (!existsSync7(config3.basePath)) {
10440
+ if (!existsSync8(config3.basePath)) {
10572
10441
  mkdirSync4(config3.basePath, { recursive: true });
10573
10442
  }
10574
10443
  } else if (row.driver === "s3") {
@@ -10600,7 +10469,7 @@ function buildConfigFromEnv(driver) {
10600
10469
  if (driver === "filesystem") {
10601
10470
  const rawPath = process.env["STORAGE_PATH"] || "./storage";
10602
10471
  config3.basePath = resolveStoragePath(rawPath, projectPath);
10603
- if (!existsSync7(config3.basePath)) {
10472
+ if (!existsSync8(config3.basePath)) {
10604
10473
  mkdirSync4(config3.basePath, { recursive: true });
10605
10474
  }
10606
10475
  } else if (driver === "s3") {
@@ -11039,7 +10908,7 @@ var init_storage_service = __esm({
11039
10908
  });
11040
10909
 
11041
10910
  // src/modules/storage/storage.entity.ts
11042
- import { useIdField as useIdField3, useTextField as useTextField8, useSelectField as useSelectField9, useUrlField, useNumberField as useNumberField4, useCheckboxField as useCheckboxField3, useJsonField as useJsonField2, useMetadataField, usePublicField } from "@gzl10/nexus-sdk/fields";
10911
+ import { useIdField as useIdField3, useTextField as useTextField8, useSelectField as useSelectField7, useUrlField, useNumberField as useNumberField5, useCheckboxField as useCheckboxField3, useJsonField as useJsonField2, useMetadataField, usePublicField } from "@gzl10/nexus-sdk/fields";
11043
10912
  var DEFAULT_MAX_SIZE2, storageConfigEntity, storageFilesEntity;
11044
10913
  var init_storage_entity = __esm({
11045
10914
  "src/modules/storage/storage.entity.ts"() {
@@ -11074,7 +10943,7 @@ var init_storage_entity = __esm({
11074
10943
  hint: { en: "Unique identifier (e.g. default_filesystem, default_s3)", es: "Identificador \xFAnico (ej: default_filesystem, default_s3)" }
11075
10944
  }),
11076
10945
  driver: {
11077
- ...useSelectField9({
10946
+ ...useSelectField7({
11078
10947
  label: { en: "Driver", es: "Controlador" },
11079
10948
  required: true,
11080
10949
  hint: { en: "Storage backend", es: "Backend de almacenamiento" },
@@ -11091,7 +10960,7 @@ var init_storage_entity = __esm({
11091
10960
  size: 500,
11092
10961
  nullable: true
11093
10962
  }),
11094
- max_file_size: useNumberField4({
10963
+ max_file_size: useNumberField5({
11095
10964
  label: { en: "Max File Size (bytes)", es: "Tama\xF1o m\xE1ximo de archivo (bytes)" },
11096
10965
  hint: { en: "Maximum size in bytes (default: 10MB = 10485760)", es: "Tama\xF1o m\xE1ximo en bytes (default: 10MB = 10485760)" },
11097
10966
  nullable: false,
@@ -11284,7 +11153,7 @@ var init_storage_entity = __esm({
11284
11153
  index: true,
11285
11154
  meta: { sortable: true, searchable: true }
11286
11155
  }),
11287
- size: useNumberField4({
11156
+ size: useNumberField5({
11288
11157
  label: { en: "Size", es: "Tama\xF1o" },
11289
11158
  required: true,
11290
11159
  nullable: false,
@@ -11826,7 +11695,7 @@ var init_storage = __esm({
11826
11695
  });
11827
11696
 
11828
11697
  // src/modules/users/users.entity.ts
11829
- import { useIdField as useIdField4, useSelectField as useSelectField10, useEmailField, usePasswordField, useTextField as useTextField9, useDatetimeField as useDatetimeField3, useCheckboxField as useCheckboxField4, useImageField as useImageField2, useNameField as useNameField2, useMetadataField as useMetadataField2, useDescriptionField as useDescriptionField2 } from "@gzl10/nexus-sdk/fields";
11698
+ import { useIdField as useIdField4, useSelectField as useSelectField8, useEmailField, usePasswordField, useTextField as useTextField9, useDatetimeField as useDatetimeField3, useCheckboxField as useCheckboxField4, useImageField as useImageField2, useNameField as useNameField2, useMetadataField as useMetadataField2, useDescriptionField as useDescriptionField2 } from "@gzl10/nexus-sdk/fields";
11830
11699
  import { z as z3 } from "zod";
11831
11700
  var userEntity, roleEntity, userRoleEntity;
11832
11701
  var init_users_entity = __esm({
@@ -11900,7 +11769,7 @@ var init_users_entity = __esm({
11900
11769
  label: { en: "Marketing Opt-in", es: "Aceptar marketing" },
11901
11770
  meta: { exportable: true, showInForm: false, showInDisplay: false }
11902
11771
  }),
11903
- locale: useSelectField10({
11772
+ locale: useSelectField8({
11904
11773
  label: { en: "Language", es: "Idioma" },
11905
11774
  options: [
11906
11775
  { value: "es", label: { en: "Spanish", es: "Espa\xF1ol" } },
@@ -11910,13 +11779,13 @@ var init_users_entity = __esm({
11910
11779
  meta: { sortable: true },
11911
11780
  defaultValue: "en"
11912
11781
  }),
11913
- timezone: useSelectField10({
11782
+ timezone: useSelectField8({
11914
11783
  label: { en: "Timezone", es: "Zona horaria" },
11915
11784
  master: "timezones",
11916
11785
  meta: { sortable: true },
11917
11786
  defaultValue: "timezones:Europe/Madrid"
11918
11787
  }),
11919
- type: useSelectField10({
11788
+ type: useSelectField8({
11920
11789
  label: { en: "Type", es: "Tipo" },
11921
11790
  defaultValue: "human",
11922
11791
  options: [
@@ -12085,7 +11954,7 @@ var init_users_entity = __esm({
12085
11954
  expose: false,
12086
11955
  fields: {
12087
11956
  id: useIdField4(),
12088
- user_id: useSelectField10({
11957
+ user_id: useSelectField8({
12089
11958
  label: { en: "User", es: "Usuario" },
12090
11959
  required: true,
12091
11960
  table: "users",
@@ -12096,7 +11965,7 @@ var init_users_entity = __esm({
12096
11965
  labelField: "name",
12097
11966
  meta: { searchable: true }
12098
11967
  }),
12099
- role_id: useSelectField10({
11968
+ role_id: useSelectField8({
12100
11969
  label: { en: "Role", es: "Rol" },
12101
11970
  required: true,
12102
11971
  table: "roles",
@@ -13040,7 +12909,7 @@ var init_users = __esm({
13040
12909
  });
13041
12910
 
13042
12911
  // src/modules/auth/auth.entity.ts
13043
- import { useIdField as useIdField5, useTextField as useTextField10, useSelectField as useSelectField11, useDatetimeField as useDatetimeField4, useEmailField as useEmailField2, useMetadataField as useMetadataField3, useExpiresAtField } from "@gzl10/nexus-sdk/fields";
12912
+ import { useIdField as useIdField5, useTextField as useTextField10, useSelectField as useSelectField9, useDatetimeField as useDatetimeField4, useEmailField as useEmailField2, useMetadataField as useMetadataField3, useExpiresAtField } from "@gzl10/nexus-sdk/fields";
13044
12913
  var refreshTokenEntity, authIdentitiesEntity;
13045
12914
  var init_auth_entity = __esm({
13046
12915
  "src/modules/auth/auth.entity.ts"() {
@@ -13121,7 +12990,7 @@ var init_auth_entity = __esm({
13121
12990
  order: 5,
13122
12991
  fields: {
13123
12992
  id: useIdField5(),
13124
- user_id: useSelectField11({
12993
+ user_id: useSelectField9({
13125
12994
  label: { en: "User", es: "Usuario" },
13126
12995
  table: "users",
13127
12996
  column: "id",
@@ -13205,6 +13074,28 @@ var init_auth_routes = __esm({
13205
13074
  }
13206
13075
  });
13207
13076
 
13077
+ // src/core/crypto/secret-resolver.ts
13078
+ import { randomBytes } from "crypto";
13079
+ function _resetSecretResolver() {
13080
+ _secret = null;
13081
+ _db = null;
13082
+ }
13083
+ function getAuthSecret() {
13084
+ if (_secret) return _secret;
13085
+ const envSecret = process.env["AUTH_SECRET"];
13086
+ if (envSecret) return envSecret;
13087
+ throw new Error("AUTH_SECRET not initialized. Call initAuthSecret() during startup.");
13088
+ }
13089
+ var _secret, _db;
13090
+ var init_secret_resolver = __esm({
13091
+ "src/core/crypto/secret-resolver.ts"() {
13092
+ "use strict";
13093
+ init_database();
13094
+ _secret = null;
13095
+ _db = null;
13096
+ }
13097
+ });
13098
+
13208
13099
  // src/modules/auth/auth.config.ts
13209
13100
  import { z as z5 } from "zod";
13210
13101
  function configError(module, errors) {
@@ -13221,12 +13112,6 @@ function parseAuthEnv() {
13221
13112
  if (!result.success) {
13222
13113
  const errors = result.error.issues.map((issue) => {
13223
13114
  const path3 = issue.path.join(".");
13224
- if (path3 === "AUTH_SECRET" && issue.code === "invalid_type") {
13225
- return "AUTH_SECRET is required. Set it in your .env file or environment.";
13226
- }
13227
- if (path3 === "AUTH_SECRET" && issue.code === "too_small") {
13228
- return `AUTH_SECRET must be at least 32 characters (current: ${String(process.env["AUTH_SECRET"]).length})`;
13229
- }
13230
13115
  return `${path3}: ${issue.message}`;
13231
13116
  });
13232
13117
  configError("auth", errors);
@@ -13245,7 +13130,7 @@ function getAuthEnv() {
13245
13130
  function getAuthConfig() {
13246
13131
  const authEnv = getAuthEnv();
13247
13132
  return {
13248
- secret: authEnv.AUTH_SECRET,
13133
+ secret: getAuthSecret(),
13249
13134
  accessExpires: authEnv.AUTH_ACCESS_EXPIRES,
13250
13135
  refreshExpires: authEnv.AUTH_REFRESH_EXPIRES,
13251
13136
  rateLimitMax: authEnv.AUTH_RATE_LIMIT_MAX,
@@ -13262,8 +13147,8 @@ var authEnvSchema, _authEnv;
13262
13147
  var init_auth_config = __esm({
13263
13148
  "src/modules/auth/auth.config.ts"() {
13264
13149
  "use strict";
13150
+ init_secret_resolver();
13265
13151
  authEnvSchema = z5.object({
13266
- AUTH_SECRET: z5.string().min(32, "AUTH_SECRET must be at least 32 characters"),
13267
13152
  AUTH_ACCESS_EXPIRES: z5.string().default("15m"),
13268
13153
  AUTH_REFRESH_EXPIRES: z5.string().default("7d"),
13269
13154
  // Rate limiting (default: 5 requests per 15 minutes)
@@ -13437,7 +13322,7 @@ var init_auth_middleware = __esm({
13437
13322
  });
13438
13323
 
13439
13324
  // src/modules/auth/auth.pat.entity.ts
13440
- import { useIdField as useIdField6, useTextField as useTextField11, useSelectField as useSelectField12, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
13325
+ import { useIdField as useIdField6, useTextField as useTextField11, useSelectField as useSelectField10, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
13441
13326
  var personalTokenEntity;
13442
13327
  var init_auth_pat_entity = __esm({
13443
13328
  "src/modules/auth/auth.pat.entity.ts"() {
@@ -13454,7 +13339,7 @@ var init_auth_pat_entity = __esm({
13454
13339
  routePrefix: "/personal-tokens",
13455
13340
  fields: {
13456
13341
  id: useIdField6(),
13457
- user_id: useSelectField12({
13342
+ user_id: useSelectField10({
13458
13343
  label: { en: "User", es: "Usuario" },
13459
13344
  table: "users",
13460
13345
  column: "id",
@@ -13489,7 +13374,7 @@ var init_auth_pat_entity = __esm({
13489
13374
  unique: true,
13490
13375
  meta: { exportable: false }
13491
13376
  }),
13492
- scope: useSelectField12({
13377
+ scope: useSelectField10({
13493
13378
  label: { en: "Permission", es: "Permiso" },
13494
13379
  required: true,
13495
13380
  options: [
@@ -13521,42 +13406,6 @@ var init_auth_pat_entity = __esm({
13521
13406
  }
13522
13407
  });
13523
13408
 
13524
- // src/modules/auth/actions/providers.action.ts
13525
- var providersAction;
13526
- var init_providers_action = __esm({
13527
- "src/modules/auth/actions/providers.action.ts"() {
13528
- "use strict";
13529
- init_auth_config();
13530
- providersAction = {
13531
- key: "providers",
13532
- label: { en: "Get Auth Providers", es: "Obtener proveedores de autenticaci\xF3n" },
13533
- icon: "mdi:account-key",
13534
- scope: "module",
13535
- hidden: true,
13536
- method: "GET",
13537
- skipAuth: true,
13538
- handler: async (ctx) => {
13539
- const providerServices = ctx.services.getBySuffix(".provider");
13540
- const results = await Promise.all(
13541
- providerServices.map(async ({ service }) => {
13542
- try {
13543
- return await service.getInfo();
13544
- } catch {
13545
- return null;
13546
- }
13547
- })
13548
- );
13549
- const providers = results.filter((info) => info !== null);
13550
- const config3 = getAuthConfig();
13551
- return {
13552
- providers,
13553
- registrationEnabled: !config3.disableRegistration
13554
- };
13555
- }
13556
- };
13557
- }
13558
- });
13559
-
13560
13409
  // src/modules/auth/actions/helpers.ts
13561
13410
  function getCookieOptions(req) {
13562
13411
  const config3 = getAuthConfig();
@@ -14113,7 +13962,6 @@ var authActions;
14113
13962
  var init_actions2 = __esm({
14114
13963
  "src/modules/auth/actions/index.ts"() {
14115
13964
  "use strict";
14116
- init_providers_action();
14117
13965
  init_login_action();
14118
13966
  init_register_action();
14119
13967
  init_forgot_password_action();
@@ -14129,7 +13977,6 @@ var init_actions2 = __esm({
14129
13977
  init_list_tokens_action();
14130
13978
  init_revoke_token_action();
14131
13979
  init_impersonate_action();
14132
- init_providers_action();
14133
13980
  init_login_action();
14134
13981
  init_register_action();
14135
13982
  init_forgot_password_action();
@@ -14145,7 +13992,6 @@ var init_actions2 = __esm({
14145
13992
  init_list_tokens_action();
14146
13993
  init_revoke_token_action();
14147
13994
  authActions = [
14148
- providersAction,
14149
13995
  loginAction,
14150
13996
  registerAction,
14151
13997
  forgotPasswordAction,
@@ -14364,7 +14210,7 @@ var init_otp_manager = __esm({
14364
14210
  });
14365
14211
 
14366
14212
  // src/modules/auth/auth.service.ts
14367
- import { createHash as createHash4, randomBytes } from "crypto";
14213
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
14368
14214
  function createAuthService(ctx) {
14369
14215
  const { errors, abilities, crypto: crypto3 } = ctx.core;
14370
14216
  const { generateId: generateId2 } = ctx.core;
@@ -14781,7 +14627,7 @@ function createAuthService(ctx) {
14781
14627
  },
14782
14628
  // === Personal Access Tokens ===
14783
14629
  async createPersonalToken(userId, input, requestInfo) {
14784
- const rawToken = "nxs_" + randomBytes(32).toString("hex");
14630
+ const rawToken = "nxs_" + randomBytes2(32).toString("hex");
14785
14631
  const tokenHash = createHash4("sha256").update(rawToken).digest("hex");
14786
14632
  const tokenPrefix = "nxs_..." + rawToken.slice(-6);
14787
14633
  const id = generateId2();
@@ -15117,8 +14963,8 @@ var init_mail_config = __esm({
15117
14963
 
15118
14964
  // src/modules/mail/mail.service.ts
15119
14965
  import nodemailer from "nodemailer";
15120
- import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
15121
- import { join as join11 } from "path";
14966
+ import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
14967
+ import { join as join12 } from "path";
15122
14968
  function getMailService() {
15123
14969
  if (!mailServiceInstance) {
15124
14970
  throw new Error("MailService not initialized. Call initMailService() first.");
@@ -15135,8 +14981,8 @@ var init_mail_service = __esm({
15135
14981
  "src/modules/mail/mail.service.ts"() {
15136
14982
  "use strict";
15137
14983
  init_mail_config();
15138
- TEMPLATE_REL_PATH = join11("public", "mail", "base.html");
15139
- LOGO_REL_PATH = join11("public", "nexus", "nexus-light-512.png");
14984
+ TEMPLATE_REL_PATH = join12("public", "mail", "base.html");
14985
+ LOGO_REL_PATH = join12("public", "nexus", "nexus-light-512.png");
15140
14986
  mailServiceInstance = null;
15141
14987
  MailService = class {
15142
14988
  transporter;
@@ -15150,10 +14996,10 @@ var init_mail_service = __esm({
15150
14996
  this.logger = logger3.child({ service: "mail" });
15151
14997
  this.loggerService = loggerService;
15152
14998
  const libPath = options?.libPath ?? process.cwd();
15153
- this.template = readFileSync6(join11(libPath, TEMPLATE_REL_PATH), "utf-8");
15154
- const logoPath = join11(libPath, LOGO_REL_PATH);
15155
- if (existsSync8(logoPath)) {
15156
- const logoBase64 = readFileSync6(logoPath).toString("base64");
14999
+ this.template = readFileSync7(join12(libPath, TEMPLATE_REL_PATH), "utf-8");
15000
+ const logoPath = join12(libPath, LOGO_REL_PATH);
15001
+ if (existsSync9(logoPath)) {
15002
+ const logoBase64 = readFileSync7(logoPath).toString("base64");
15157
15003
  this.defaultLogoUrl = `data:image/png;base64,${logoBase64}`;
15158
15004
  } else {
15159
15005
  this.defaultLogoUrl = "";
@@ -15245,7 +15091,7 @@ var init_mail_service = __esm({
15245
15091
  });
15246
15092
 
15247
15093
  // src/modules/mail/mail.entity.ts
15248
- import { useIdField as useIdField7, useTextField as useTextField12, useSelectField as useSelectField13, useNumberField as useNumberField5, useSwitchField as useSwitchField4, useEmailField as useEmailField3, usePasswordField as usePasswordField2, useTextareaField as useTextareaField3, useTagsField as useTagsField2, useDatetimeField as useDatetimeField6 } from "@gzl10/nexus-sdk/fields";
15094
+ import { useIdField as useIdField7, useTextField as useTextField12, useSelectField as useSelectField11, useNumberField as useNumberField6, useSwitchField as useSwitchField3, useEmailField as useEmailField3, usePasswordField as usePasswordField2, useTextareaField as useTextareaField3, useTagsField as useTagsField2, useDatetimeField as useDatetimeField6 } from "@gzl10/nexus-sdk/fields";
15249
15095
  import nodemailer2 from "nodemailer";
15250
15096
  async function getMailConfigFromDB(ctx) {
15251
15097
  const configService = ctx.services["config"];
@@ -15314,12 +15160,12 @@ var init_mail_entity = __esm({
15314
15160
  nullable: false,
15315
15161
  hint: { en: "Default: SMTP_HOST env var", es: "Por defecto: variable SMTP_HOST" }
15316
15162
  }),
15317
- port: useNumberField5({
15163
+ port: useNumberField6({
15318
15164
  label: { en: "Port", es: "Puerto" },
15319
15165
  nullable: false,
15320
15166
  hint: { en: "Default: SMTP_PORT env var", es: "Por defecto: variable SMTP_PORT" }
15321
15167
  }),
15322
- secure: useSwitchField4({
15168
+ secure: useSwitchField3({
15323
15169
  label: { en: "TLS/SSL", es: "TLS/SSL" },
15324
15170
  hint: { en: "Default: SMTP_SECURE env var", es: "Por defecto: variable SMTP_SECURE" }
15325
15171
  }),
@@ -15522,7 +15368,7 @@ var init_mail_entity = __esm({
15522
15368
  meta: { searchable: true, sortable: true }
15523
15369
  }),
15524
15370
  status: {
15525
- ...useSelectField13({
15371
+ ...useSelectField11({
15526
15372
  label: { en: "Status", es: "Estado" },
15527
15373
  options: [
15528
15374
  { value: "pending", label: { en: "Pending", es: "Pendiente" } },
@@ -15546,7 +15392,7 @@ var init_mail_entity = __esm({
15546
15392
  label: { en: "Error", es: "Error" },
15547
15393
  nullable: true
15548
15394
  }),
15549
- sent_by: useSelectField13({
15395
+ sent_by: useSelectField11({
15550
15396
  label: { en: "Sent by", es: "Enviado por" },
15551
15397
  table: "users",
15552
15398
  column: "id",
@@ -16172,7 +16018,7 @@ var init_toggle_plugin_action = __esm({
16172
16018
  });
16173
16019
 
16174
16020
  // src/modules/plugins/plugins.entity.ts
16175
- import { useTextField as useTextField13, useSelectField as useSelectField14, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16021
+ import { useTextField as useTextField13, useSelectField as useSelectField12, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16176
16022
  import { OFFICIAL_PLUGINS } from "@gzl10/nexus-sdk";
16177
16023
  var allowPluginManagement, pluginsEntity;
16178
16024
  var init_plugins_entity = __esm({
@@ -16214,7 +16060,7 @@ var init_plugins_entity = __esm({
16214
16060
  size: 20,
16215
16061
  nullable: false
16216
16062
  }),
16217
- category: useSelectField14({
16063
+ category: useSelectField12({
16218
16064
  label: { en: "Category", es: "Categor\xEDa" },
16219
16065
  options: [
16220
16066
  { value: "content", label: { en: "Content", es: "Contenido" } },
@@ -16293,7 +16139,7 @@ var init_plugins_entity = __esm({
16293
16139
 
16294
16140
  // src/modules/plugins/plugins.routes.ts
16295
16141
  import { Router } from "express";
16296
- import { existsSync as existsSync9 } from "fs";
16142
+ import { existsSync as existsSync10 } from "fs";
16297
16143
  function createPluginRoutes(ctx) {
16298
16144
  const router = Router();
16299
16145
  let imageMap = null;
@@ -16301,7 +16147,7 @@ function createPluginRoutes(ctx) {
16301
16147
  if (!imageMap) {
16302
16148
  const discovered = await ctx.core.plugins.discover();
16303
16149
  imageMap = new Map(
16304
- discovered.filter((p) => p.image && existsSync9(p.image)).map((p) => [p.code, p.image])
16150
+ discovered.filter((p) => p.image && existsSync10(p.image)).map((p) => [p.code, p.image])
16305
16151
  );
16306
16152
  }
16307
16153
  return imageMap;
@@ -16356,7 +16202,7 @@ var init_plugins = __esm({
16356
16202
  import {
16357
16203
  useIdField as useIdField8,
16358
16204
  useTextField as useTextField14,
16359
- useSelectField as useSelectField15,
16205
+ useSelectField as useSelectField13,
16360
16206
  useTextareaField as useTextareaField4,
16361
16207
  useJsonField as useJsonField3,
16362
16208
  useDatetimeField as useDatetimeField7,
@@ -16398,7 +16244,7 @@ var init_audit_entity = __esm({
16398
16244
  }),
16399
16245
  validation: { min: 1, max: 100 }
16400
16246
  },
16401
- actor_id: useSelectField15({
16247
+ actor_id: useSelectField13({
16402
16248
  label: { en: "Actor", es: "Actor" },
16403
16249
  table: "users",
16404
16250
  column: "id",
@@ -16675,6 +16521,7 @@ import jwt3 from "jsonwebtoken";
16675
16521
  var init_jwt = __esm({
16676
16522
  "src/core/jwt/index.ts"() {
16677
16523
  "use strict";
16524
+ init_secret_resolver();
16678
16525
  }
16679
16526
  });
16680
16527
 
@@ -17086,6 +16933,7 @@ var init_socket = __esm({
17086
16933
  "use strict";
17087
16934
  init_emitter();
17088
16935
  init_logger();
16936
+ init_secret_resolver();
17089
16937
  io = null;
17090
16938
  userSockets = /* @__PURE__ */ new Map();
17091
16939
  socketUsers = /* @__PURE__ */ new Map();
@@ -17279,8 +17127,8 @@ var init_port_check = __esm({
17279
17127
  });
17280
17128
 
17281
17129
  // src/db/seed-runner.ts
17282
- import { existsSync as existsSync10 } from "fs";
17283
- import { join as join12 } from "path";
17130
+ import { existsSync as existsSync11 } from "fs";
17131
+ import { join as join13 } from "path";
17284
17132
  import { pathToFileURL as pathToFileURL2 } from "url";
17285
17133
  var init_seed_runner = __esm({
17286
17134
  "src/db/seed-runner.ts"() {
@@ -18305,7 +18153,7 @@ var init_migration_helpers = __esm({
18305
18153
  // src/db/migration-generator.ts
18306
18154
  import path2 from "path";
18307
18155
  import fs2 from "fs/promises";
18308
- import { readFileSync as readFileSync7, mkdirSync as mkdirSync5, realpathSync } from "fs";
18156
+ import { readFileSync as readFileSync8, mkdirSync as mkdirSync5, realpathSync } from "fs";
18309
18157
  function getColumnIndexBytes(field) {
18310
18158
  if (!field?.db) return 255 * MYSQL_BYTES_PER_CHAR;
18311
18159
  const size = field.db.size ?? 255;
@@ -18397,7 +18245,7 @@ function resolvePrefix(scope) {
18397
18245
  function detectDefaultScope() {
18398
18246
  try {
18399
18247
  const pkgPath = path2.join(getProjectPath(), "package.json");
18400
- const pkg3 = JSON.parse(readFileSync7(pkgPath, "utf-8"));
18248
+ const pkg3 = JSON.parse(readFileSync8(pkgPath, "utf-8"));
18401
18249
  const pkgName = pkg3?.name;
18402
18250
  if (pkgName === "@gzl10/nexus-backend") {
18403
18251
  return "core";
@@ -19186,7 +19034,7 @@ var init_db = __esm({
19186
19034
  // src/core/tunnel.ts
19187
19035
  import { spawn, execSync } from "child_process";
19188
19036
  import { writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
19189
- import { join as join13 } from "path";
19037
+ import { join as join14 } from "path";
19190
19038
  import { tmpdir } from "os";
19191
19039
  function stopTunnel() {
19192
19040
  if (tunnelProcess) {
@@ -19290,6 +19138,7 @@ async function stop() {
19290
19138
  resetConfigCache();
19291
19139
  clearCustomCaslRules();
19292
19140
  clearSeedPermissions();
19141
+ _resetSecretResolver();
19293
19142
  await resetServeSPA();
19294
19143
  return;
19295
19144
  }
@@ -19325,6 +19174,7 @@ async function stop() {
19325
19174
  resetConfigCache();
19326
19175
  clearCustomCaslRules();
19327
19176
  clearSeedPermissions();
19177
+ _resetSecretResolver();
19328
19178
  await resetServeSPA();
19329
19179
  currentConfig = void 0;
19330
19180
  server = null;
@@ -19385,6 +19235,7 @@ var init_server = __esm({
19385
19235
  init_ensure_system_tables();
19386
19236
  init_load_config();
19387
19237
  init_tunnel();
19238
+ init_secret_resolver();
19388
19239
  server = null;
19389
19240
  gracefulShutdownRegistered = false;
19390
19241
  setupGracefulShutdown();
@@ -19440,10 +19291,11 @@ var init_hash = __esm({
19440
19291
  });
19441
19292
 
19442
19293
  // src/core/crypto/symmetric.ts
19443
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, hkdfSync } from "crypto";
19294
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, hkdfSync } from "crypto";
19444
19295
  var init_symmetric = __esm({
19445
19296
  "src/core/crypto/symmetric.ts"() {
19446
19297
  "use strict";
19298
+ init_secret_resolver();
19447
19299
  }
19448
19300
  });
19449
19301
 
@@ -19555,6 +19407,13 @@ var init_events_api = __esm({
19555
19407
  }
19556
19408
  });
19557
19409
 
19410
+ // src/engine/capabilities-registry.ts
19411
+ var init_capabilities_registry = __esm({
19412
+ "src/engine/capabilities-registry.ts"() {
19413
+ "use strict";
19414
+ }
19415
+ });
19416
+
19558
19417
  // src/engine/context.ts
19559
19418
  import { ForbiddenError as CASLForbiddenError3, subject } from "@casl/ability";
19560
19419
  import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2, DEFAULT_LOCALES as DEFAULT_LOCALES2 } from "@gzl10/nexus-sdk";
@@ -19581,6 +19440,7 @@ var init_context = __esm({
19581
19440
  init_plugin_ops();
19582
19441
  init_load_config();
19583
19442
  init_events_api();
19443
+ init_capabilities_registry();
19584
19444
  init_seed_runner();
19585
19445
  init_cache_manager();
19586
19446
  sharedCacheManager = null;
@@ -19599,6 +19459,7 @@ var init_engine = __esm({
19599
19459
  init_subject_extractor();
19600
19460
  init_definition_extractors();
19601
19461
  init_context();
19462
+ init_capabilities_registry();
19602
19463
  }
19603
19464
  });
19604
19465
 
@@ -19616,8 +19477,8 @@ __export(migrate_commands_exports, {
19616
19477
  handleUp: () => handleUp,
19617
19478
  loadModulesForMigration: () => loadModulesForMigration
19618
19479
  });
19619
- import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
19620
- import { join as join14 } from "path";
19480
+ import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
19481
+ import { join as join15 } from "path";
19621
19482
  import { pathToFileURL as pathToFileURL3 } from "url";
19622
19483
  import Table from "cli-table3";
19623
19484
  import { consola as consola2 } from "consola";
@@ -19627,15 +19488,15 @@ function scopeToSourceId(scope) {
19627
19488
  }
19628
19489
  async function loadSelfPlugin() {
19629
19490
  const projectPath2 = getProjectPath();
19630
- const pkgPath = join14(projectPath2, "package.json");
19631
- if (!existsSync11(pkgPath)) return;
19491
+ const pkgPath = join15(projectPath2, "package.json");
19492
+ if (!existsSync12(pkgPath)) return;
19632
19493
  try {
19633
- const pkg3 = JSON.parse(readFileSync8(pkgPath, "utf-8"));
19494
+ const pkg3 = JSON.parse(readFileSync9(pkgPath, "utf-8"));
19634
19495
  const pkgName = pkg3?.name;
19635
19496
  if (!pkgName || !/nexus-plugin-/.test(pkgName)) return;
19636
- const srcEntry = join14(projectPath2, "src", "index.ts");
19637
- const distEntry = join14(projectPath2, "dist", "index.js");
19638
- if (existsSync11(srcEntry)) {
19497
+ const srcEntry = join15(projectPath2, "src", "index.ts");
19498
+ const distEntry = join15(projectPath2, "dist", "index.js");
19499
+ if (existsSync12(srcEntry)) {
19639
19500
  try {
19640
19501
  const { tsImport } = await import("tsx/esm/api");
19641
19502
  const mod = await tsImport(
@@ -19645,7 +19506,7 @@ async function loadSelfPlugin() {
19645
19506
  const manifest = extractPluginManifest(mod);
19646
19507
  if (manifest) {
19647
19508
  if (!manifest.migrationsDir) {
19648
- manifest.migrationsDir = join14(projectPath2, "migrations");
19509
+ manifest.migrationsDir = join15(projectPath2, "migrations");
19649
19510
  }
19650
19511
  registerPlugin(manifest);
19651
19512
  return;
@@ -19654,13 +19515,13 @@ async function loadSelfPlugin() {
19654
19515
  console.error(` \u26A0 Failed to load plugin src/index.ts: ${err.message}`);
19655
19516
  }
19656
19517
  }
19657
- if (existsSync11(distEntry)) {
19518
+ if (existsSync12(distEntry)) {
19658
19519
  try {
19659
19520
  const mod = await import(pathToFileURL3(distEntry).href);
19660
19521
  const manifest = extractPluginManifest(mod);
19661
19522
  if (manifest) {
19662
19523
  if (!manifest.migrationsDir) {
19663
- manifest.migrationsDir = join14(projectPath2, "migrations");
19524
+ manifest.migrationsDir = join15(projectPath2, "migrations");
19664
19525
  }
19665
19526
  registerPlugin(manifest);
19666
19527
  return;
@@ -19979,7 +19840,7 @@ __export(db_commands_exports, {
19979
19840
  handleDbWipe: () => handleDbWipe
19980
19841
  });
19981
19842
  import { spawn as spawn2 } from "child_process";
19982
- import { isAbsolute as isAbsolute3, join as join15 } from "path";
19843
+ import { isAbsolute as isAbsolute3, join as join16 } from "path";
19983
19844
  import { statSync as statSync2 } from "fs";
19984
19845
  import { consola as consola3 } from "consola";
19985
19846
  async function handleDbWipe(options) {
@@ -20067,7 +19928,7 @@ function handleDbShell() {
20067
19928
  if (dbType === "sqlite") {
20068
19929
  let filename = url.replace(/^(file:|sqlite:)/, "");
20069
19930
  if (!isAbsolute3(filename)) {
20070
- filename = join15(getProjectPath(), "data", filename);
19931
+ filename = join16(getProjectPath(), "data", filename);
20071
19932
  }
20072
19933
  cmd = "sqlite3";
20073
19934
  args = [filename];
@@ -20112,7 +19973,7 @@ async function collectDbInfo() {
20112
19973
  if (url !== ":memory:" && !url.includes(":memory:")) {
20113
19974
  let filename = url.replace(/^(file:|sqlite:)/, "");
20114
19975
  if (!isAbsolute3(filename)) {
20115
- filename = join15(getProjectPath(), "data", filename);
19976
+ filename = join16(getProjectPath(), "data", filename);
20116
19977
  }
20117
19978
  try {
20118
19979
  const stat2 = statSync2(filename);
@@ -20169,8 +20030,8 @@ __export(plugin_commands_exports, {
20169
20030
  handlePluginList: () => handlePluginList,
20170
20031
  handlePluginRemove: () => handlePluginRemove
20171
20032
  });
20172
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
20173
- import { join as join16 } from "path";
20033
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
20034
+ import { join as join17 } from "path";
20174
20035
  import Table2 from "cli-table3";
20175
20036
  import { consola as consola4 } from "consola";
20176
20037
  import { OFFICIAL_PLUGINS as OFFICIAL_PLUGINS2 } from "@gzl10/nexus-sdk";
@@ -20178,10 +20039,10 @@ function pluginLabel(name) {
20178
20039
  return shortPluginName(name).split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
20179
20040
  }
20180
20041
  function readPluginVersion(projectPath2, pkgName) {
20181
- const pkgJsonPath = join16(projectPath2, "node_modules", pkgName, "package.json");
20182
- if (!existsSync12(pkgJsonPath)) return null;
20042
+ const pkgJsonPath = join17(projectPath2, "node_modules", pkgName, "package.json");
20043
+ if (!existsSync13(pkgJsonPath)) return null;
20183
20044
  try {
20184
- const pkg3 = JSON.parse(readFileSync9(pkgJsonPath, "utf-8"));
20045
+ const pkg3 = JSON.parse(readFileSync10(pkgJsonPath, "utf-8"));
20185
20046
  return pkg3.version ?? null;
20186
20047
  } catch {
20187
20048
  return null;
@@ -20467,19 +20328,19 @@ var sync_commands_exports = {};
20467
20328
  __export(sync_commands_exports, {
20468
20329
  handleSyncCommands: () => handleSyncCommands
20469
20330
  });
20470
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
20471
- import { join as join17, basename as basename4 } from "path";
20331
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
20332
+ import { join as join18, basename as basename4 } from "path";
20472
20333
  import { homedir } from "os";
20473
20334
  import { createConsola } from "consola";
20474
20335
  async function handleSyncCommands(options) {
20475
- const sourceDir = join17(getLibPath(), "claude-commands");
20476
- const targetDir = join17(homedir(), ".claude", "commands");
20477
- if (!existsSync13(sourceDir)) {
20336
+ const sourceDir = join18(getLibPath(), "claude-commands");
20337
+ const targetDir = join18(homedir(), ".claude", "commands");
20338
+ if (!existsSync14(sourceDir)) {
20478
20339
  logger2.warn(`Commands source not found: ${sourceDir}`);
20479
20340
  logger2.info("Make sure @gzl10/nexus-backend is properly installed.");
20480
20341
  return;
20481
20342
  }
20482
- if (!existsSync13(targetDir)) {
20343
+ if (!existsSync14(targetDir)) {
20483
20344
  if (options.dryRun) {
20484
20345
  logger2.info(`Would create: ${targetDir}`);
20485
20346
  } else {
@@ -20494,16 +20355,16 @@ async function handleSyncCommands(options) {
20494
20355
  }
20495
20356
  const result = { added: [], updated: [], unchanged: [] };
20496
20357
  for (const file of sourceFiles) {
20497
- const sourcePath = join17(sourceDir, file);
20498
- const targetPath = join17(targetDir, file);
20499
- const sourceContent = readFileSync10(sourcePath, "utf-8");
20500
- if (!existsSync13(targetPath)) {
20358
+ const sourcePath = join18(sourceDir, file);
20359
+ const targetPath = join18(targetDir, file);
20360
+ const sourceContent = readFileSync11(sourcePath, "utf-8");
20361
+ if (!existsSync14(targetPath)) {
20501
20362
  if (!options.dryRun) {
20502
20363
  writeFileSync3(targetPath, sourceContent, "utf-8");
20503
20364
  }
20504
20365
  result.added.push(file);
20505
20366
  } else {
20506
- const targetContent = readFileSync10(targetPath, "utf-8");
20367
+ const targetContent = readFileSync11(targetPath, "utf-8");
20507
20368
  if (sourceContent !== targetContent) {
20508
20369
  if (!options.dryRun) {
20509
20370
  writeFileSync3(targetPath, sourceContent, "utf-8");
@@ -20549,8 +20410,8 @@ __export(seed_commands_exports, {
20549
20410
  handleSeedExport: () => handleSeedExport,
20550
20411
  importSeedFiles: () => importSeedFiles
20551
20412
  });
20552
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readdirSync as readdirSync3, readFileSync as readFileSync11 } from "fs";
20553
- import { join as join18, basename as basename5 } from "path";
20413
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "fs";
20414
+ import { join as join19, basename as basename5 } from "path";
20554
20415
  import { consola as consola5 } from "consola";
20555
20416
  function deserializeJsonFields(record, fields) {
20556
20417
  const result = { ...record };
@@ -20579,7 +20440,7 @@ async function handleSeedExport(entity) {
20579
20440
  const db3 = getDb();
20580
20441
  try {
20581
20442
  const modules = getOrderedModules();
20582
- const seedDir = join18(getProjectPath(), "data", "seeds");
20443
+ const seedDir = join19(getProjectPath(), "data", "seeds");
20583
20444
  const seedableEntities = [];
20584
20445
  for (const mod of modules) {
20585
20446
  for (const def of mod.definitions ?? []) {
@@ -20600,7 +20461,7 @@ async function handleSeedExport(entity) {
20600
20461
  }
20601
20462
  return;
20602
20463
  }
20603
- if (!existsSync14(seedDir)) {
20464
+ if (!existsSync15(seedDir)) {
20604
20465
  mkdirSync7(seedDir, { recursive: true });
20605
20466
  }
20606
20467
  for (const { module: modName, table, fields } of seedableEntities) {
@@ -20612,7 +20473,7 @@ async function handleSeedExport(entity) {
20612
20473
  const exported = rows.map(
20613
20474
  (row) => deserializeJsonFields(row, fields)
20614
20475
  );
20615
- const filePath = join18(seedDir, `${table}.json`);
20476
+ const filePath = join19(seedDir, `${table}.json`);
20616
20477
  writeFileSync4(filePath, JSON.stringify(exported, null, 2) + "\n", "utf-8");
20617
20478
  consola5.success(`${table}: exported ${rows.length} records to data/seeds/${table}.json (module: ${modName})`);
20618
20479
  }
@@ -20624,8 +20485,8 @@ async function handleSeedExport(entity) {
20624
20485
  }
20625
20486
  }
20626
20487
  async function importSeedFiles(db3, modules, logger3) {
20627
- const seedDir = join18(getProjectPath(), "data", "seeds");
20628
- if (!existsSync14(seedDir)) return;
20488
+ const seedDir = join19(getProjectPath(), "data", "seeds");
20489
+ if (!existsSync15(seedDir)) return;
20629
20490
  const files = readdirSync3(seedDir).filter((f) => f.endsWith(".json"));
20630
20491
  if (files.length === 0) return;
20631
20492
  const seedableDefs = /* @__PURE__ */ new Map();
@@ -20646,8 +20507,8 @@ async function importSeedFiles(db3, modules, logger3) {
20646
20507
  logger3.debug(`data/seeds/${file}: skipped (entity "${table}" is not seedable)`);
20647
20508
  continue;
20648
20509
  }
20649
- const filePath = join18(seedDir, file);
20650
- const raw = readFileSync11(filePath, "utf-8");
20510
+ const filePath = join19(seedDir, file);
20511
+ const raw = readFileSync12(filePath, "utf-8");
20651
20512
  let records;
20652
20513
  try {
20653
20514
  records = JSON.parse(raw);
@@ -20694,12 +20555,12 @@ if (!nodeEnv || nodeEnv === "development") {
20694
20555
  }
20695
20556
 
20696
20557
  // src/cli.ts
20697
- import { readFileSync as readFileSync12 } from "fs";
20558
+ import { readFileSync as readFileSync13 } from "fs";
20698
20559
  import { fileURLToPath as fileURLToPath3 } from "url";
20699
- import { dirname as dirname8, join as join19 } from "path";
20560
+ import { dirname as dirname8, join as join20 } from "path";
20700
20561
  import { Command } from "commander";
20701
20562
  var __dirname2 = dirname8(fileURLToPath3(import.meta.url));
20702
- var pkg2 = JSON.parse(readFileSync12(join19(__dirname2, "..", "package.json"), "utf-8"));
20563
+ var pkg2 = JSON.parse(readFileSync13(join20(__dirname2, "..", "package.json"), "utf-8"));
20703
20564
  var program = new Command();
20704
20565
  program.name("nexus").description("Nexus Backend CLI").version(pkg2.version);
20705
20566
  program.command("info").description("Show Nexus environment, database, plugins, and paths").option("--json", "Output as JSON").action(async (options) => {