@gzl10/nexus-backend 0.18.0 → 0.19.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/index.js CHANGED
@@ -14,7 +14,7 @@ var init_package = __esm({
14
14
  "package.json"() {
15
15
  package_default = {
16
16
  name: "@gzl10/nexus-backend",
17
- version: "0.18.0",
17
+ version: "0.19.0",
18
18
  description: "Backend as a Service (BaaS) with Express 5, Knex and CASL",
19
19
  type: "module",
20
20
  main: "./dist/index.js",
@@ -26,6 +26,14 @@ var init_package = __esm({
26
26
  ".": {
27
27
  import: "./dist/index.js",
28
28
  types: "./dist/index.d.ts"
29
+ },
30
+ "./testing": {
31
+ import: "./dist/testing/index.js",
32
+ types: "./dist/testing/index.d.ts"
33
+ },
34
+ "./migrations": {
35
+ import: "./dist/migration-helpers/index.js",
36
+ types: "./dist/migration-helpers/index.d.ts"
29
37
  }
30
38
  },
31
39
  files: [
@@ -131,9 +139,11 @@ var init_package = __esm({
131
139
  "@types/cookie-parser": "^1.4.10",
132
140
  "@types/cors": "^2.8.19",
133
141
  "@types/express": "^5.0.6",
142
+ "@types/express-serve-static-core": "^5.1.1",
134
143
  "@types/jsonwebtoken": "^9.0.10",
135
144
  "@types/multer": "^2.1.0",
136
145
  "@types/nodemailer": "^7.0.11",
146
+ "@types/qs": "^6.15.0",
137
147
  "@types/supertest": "^6.0.3",
138
148
  "pino-pretty": "^13.1.3",
139
149
  "socket.io-client": "^4.8.3",
@@ -142,11 +152,15 @@ var init_package = __esm({
142
152
  vite: "^8.0.3"
143
153
  },
144
154
  peerDependencies: {
145
- vite: ">=6.0.0"
155
+ vite: ">=6.0.0",
156
+ vitest: ">=3.0.0"
146
157
  },
147
158
  peerDependenciesMeta: {
148
159
  vite: {
149
160
  optional: true
161
+ },
162
+ vitest: {
163
+ optional: true
150
164
  }
151
165
  },
152
166
  publishConfig: {
@@ -1048,6 +1062,14 @@ async function isWorkspacePackage(name) {
1048
1062
  }
1049
1063
  }
1050
1064
  async function installPlugin(name, opts) {
1065
+ const projectPath2 = opts?.projectPath ?? process.cwd();
1066
+ const pkgPath = join2(projectPath2, "package.json");
1067
+ if (existsSync2(pkgPath)) {
1068
+ const pkg3 = JSON.parse(readFileSync(pkgPath, "utf-8"));
1069
+ if (pkg3.name === "@gzl10/nexus-backend") {
1070
+ throw new Error("Cannot install plugins inside @gzl10/nexus-backend. Run this command from a Nexus project directory.");
1071
+ }
1072
+ }
1051
1073
  const isWorkspace = await isWorkspacePackage(name);
1052
1074
  let pkg2;
1053
1075
  if (opts?.version) {
@@ -1064,11 +1086,28 @@ async function installPlugin(name, opts) {
1064
1086
  plugins[name] = { enabled: true };
1065
1087
  writePluginsFile(plugins, filePath);
1066
1088
  }
1089
+ function isPackageInstalled(name, projectPath2) {
1090
+ const base = projectPath2 || process.cwd();
1091
+ const pkgPath = join2(base, "package.json");
1092
+ try {
1093
+ const pkg2 = JSON.parse(readFileSync(pkgPath, "utf-8"));
1094
+ return !!(pkg2.dependencies?.[name] || pkg2.devDependencies?.[name]);
1095
+ } catch {
1096
+ return false;
1097
+ }
1098
+ }
1067
1099
  async function uninstallPlugin(name, projectPath2) {
1068
- await execAsync(`pnpm remove ${name}`);
1069
1100
  const filePath = getPluginsFilePath(projectPath2);
1070
1101
  const plugins = readPluginsFile(filePath);
1071
- if (name in plugins) {
1102
+ const isRegistered = name in plugins;
1103
+ const isInstalled = isPackageInstalled(name, projectPath2);
1104
+ if (!isRegistered && !isInstalled) {
1105
+ throw new Error(`Plugin ${shortPluginName(name)} is not installed`);
1106
+ }
1107
+ if (isInstalled) {
1108
+ await execAsync(`pnpm remove ${name}`);
1109
+ }
1110
+ if (isRegistered) {
1072
1111
  delete plugins[name];
1073
1112
  writePluginsFile(plugins, filePath);
1074
1113
  }
@@ -2068,6 +2107,7 @@ var init_definitions = __esm({
2068
2107
  };
2069
2108
  mastersEntity = {
2070
2109
  table: "masters",
2110
+ seedable: true,
2071
2111
  routePrefix: "/",
2072
2112
  label: { en: "Master", es: "Maestro" },
2073
2113
  labelPlural: { en: "Masters", es: "Maestros" },
@@ -2099,14 +2139,15 @@ var init_definitions = __esm({
2099
2139
  meta: { sortable: true, searchable: true }
2100
2140
  }),
2101
2141
  label: useLocalizedField({ label: { en: "Name", es: "Nombre" } }),
2102
- order: orderField,
2142
+ order: { ...orderField, meta: { ...orderField.meta, showInDisplay: false } },
2103
2143
  is_active: isActiveField,
2104
2144
  metadata: useJsonField({
2105
2145
  label: { en: "Metadata", es: "Metadatos" },
2106
2146
  hint: {
2107
2147
  en: "Type-specific fields (symbol, flag, etc.)",
2108
2148
  es: "Campos espec\xEDficos del tipo"
2109
- }
2149
+ },
2150
+ meta: { showInDisplay: false }
2110
2151
  })
2111
2152
  },
2112
2153
  hooks: () => ({
@@ -2115,6 +2156,12 @@ var init_definitions = __esm({
2115
2156
  data["id"] = `${data["type"]}:${data["code"]}`;
2116
2157
  }
2117
2158
  return data;
2159
+ },
2160
+ beforeUpdate: async (_id, data) => {
2161
+ if ("type" in data || "code" in data) {
2162
+ throw new Error("Cannot update type or code of a master record. Delete and recreate instead.");
2163
+ }
2164
+ return data;
2118
2165
  }
2119
2166
  }),
2120
2167
  defaultSort: { field: "order", order: "asc" },
@@ -2166,7 +2213,7 @@ function createMasterRegistry() {
2166
2213
  await ctx.db.knex("masters").insert(row).onConflict(["type", "code"]).merge();
2167
2214
  }
2168
2215
  } else {
2169
- await ctx.db.knex("masters").insert(rows);
2216
+ await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
2170
2217
  }
2171
2218
  }
2172
2219
  },
@@ -8599,15 +8646,12 @@ var init_document_types = __esm({
8599
8646
  }
8600
8647
  });
8601
8648
 
8602
- // src/modules/masters/index.ts
8603
- var PREDEFINED_MASTERS, mastersModule;
8604
- var init_masters = __esm({
8605
- "src/modules/masters/index.ts"() {
8649
+ // src/modules/masters/constants.ts
8650
+ var DEFAULT_MASTER_TYPES, PREDEFINED_MASTERS;
8651
+ var init_constants = __esm({
8652
+ "src/modules/masters/constants.ts"() {
8606
8653
  "use strict";
8607
- init_definitions();
8608
- init_registry2();
8609
- init_definitions();
8610
- init_registry2();
8654
+ DEFAULT_MASTER_TYPES = ["languages", "timezones"];
8611
8655
  PREDEFINED_MASTERS = {
8612
8656
  currencies: () => Promise.resolve().then(() => (init_currencies(), currencies_exports)).then((m2) => m2.default),
8613
8657
  languages: () => Promise.resolve().then(() => (init_languages(), languages_exports)).then((m2) => m2.default),
@@ -8624,15 +8668,137 @@ var init_masters = __esm({
8624
8668
  "phone-prefixes": () => Promise.resolve().then(() => (init_phone_prefixes(), phone_prefixes_exports)).then((m2) => m2.default),
8625
8669
  "document-types": () => Promise.resolve().then(() => (init_document_types(), document_types_exports)).then((m2) => m2.default)
8626
8670
  };
8671
+ }
8672
+ });
8673
+
8674
+ // src/modules/masters/actions/install-type.ts
8675
+ import { useSelectField as useSelectField2 } from "@gzl10/nexus-sdk/fields";
8676
+ var installTypeAction;
8677
+ var init_install_type = __esm({
8678
+ "src/modules/masters/actions/install-type.ts"() {
8679
+ "use strict";
8680
+ init_constants();
8681
+ installTypeAction = {
8682
+ key: "install-type",
8683
+ scope: "module",
8684
+ label: { en: "Install Master Type", es: "Instalar Tipo de Maestro" },
8685
+ icon: "mdi:database-plus",
8686
+ variant: "primary",
8687
+ output: {},
8688
+ input: {
8689
+ type: useSelectField2({
8690
+ label: { en: "Master Type", es: "Tipo de Maestro" },
8691
+ required: true,
8692
+ options: Object.keys(PREDEFINED_MASTERS).filter((t) => !DEFAULT_MASTER_TYPES.includes(t)).map((t) => ({
8693
+ value: t,
8694
+ label: t.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
8695
+ }))
8696
+ })
8697
+ },
8698
+ handler: async (ctx, input) => {
8699
+ if (process.env["NODE_ENV"] === "production") {
8700
+ throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
8701
+ }
8702
+ const { type: type2 } = input;
8703
+ const loader = PREDEFINED_MASTERS[type2];
8704
+ if (!loader) {
8705
+ throw new ctx.core.errors.AppError(`Unknown predefined master type: ${type2}`, 400);
8706
+ }
8707
+ const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
8708
+ if (existing) {
8709
+ throw new ctx.core.errors.ConflictError(`Master type "${type2}" is already installed`);
8710
+ }
8711
+ const entries = await loader();
8712
+ const rows = entries.map((entry, i) => ({
8713
+ id: `${type2}:${entry.code}`,
8714
+ type: type2,
8715
+ code: entry.code,
8716
+ label: JSON.stringify(typeof entry.label === "string" ? { en: entry.label } : entry.label),
8717
+ order: entry.order ?? i,
8718
+ is_active: entry.is_active ?? true,
8719
+ metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
8720
+ }));
8721
+ await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
8722
+ return { installed: type2, count: rows.length };
8723
+ }
8724
+ };
8725
+ }
8726
+ });
8727
+
8728
+ // src/modules/masters/actions/uninstall-type.ts
8729
+ import { useTextField as useTextField3 } from "@gzl10/nexus-sdk/fields";
8730
+ var uninstallTypeAction;
8731
+ var init_uninstall_type = __esm({
8732
+ "src/modules/masters/actions/uninstall-type.ts"() {
8733
+ "use strict";
8734
+ init_constants();
8735
+ uninstallTypeAction = {
8736
+ key: "uninstall-type",
8737
+ scope: "module",
8738
+ label: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
8739
+ icon: "mdi:database-minus",
8740
+ variant: "danger",
8741
+ output: {},
8742
+ confirm: {
8743
+ type: "simple",
8744
+ title: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
8745
+ message: {
8746
+ en: "This will delete ALL records of the selected type. This action cannot be undone.",
8747
+ es: "Esto eliminar\xE1 TODOS los registros del tipo seleccionado. Esta acci\xF3n no se puede deshacer."
8748
+ }
8749
+ },
8750
+ input: {
8751
+ type: useTextField3({
8752
+ label: { en: "Type slug to uninstall", es: "Slug del tipo a desinstalar" },
8753
+ required: true,
8754
+ hint: {
8755
+ en: 'Enter the master type slug (e.g., "currencies", "countries")',
8756
+ es: 'Introduce el slug del tipo (ej: "currencies", "countries")'
8757
+ }
8758
+ })
8759
+ },
8760
+ handler: async (ctx, input) => {
8761
+ if (process.env["NODE_ENV"] === "production") {
8762
+ throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
8763
+ }
8764
+ const { type: type2 } = input;
8765
+ if (DEFAULT_MASTER_TYPES.includes(type2)) {
8766
+ throw new ctx.core.errors.AppError(`Cannot uninstall default master type "${type2}" (required by core)`, 400);
8767
+ }
8768
+ const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
8769
+ if (!existing) {
8770
+ throw new ctx.core.errors.NotFoundError(`Master type "${type2}" is not installed`);
8771
+ }
8772
+ const deleted = await ctx.db.knex("masters").where({ type: type2 }).del();
8773
+ return { uninstalled: type2, count: deleted };
8774
+ }
8775
+ };
8776
+ }
8777
+ });
8778
+
8779
+ // src/modules/masters/index.ts
8780
+ var mastersModule;
8781
+ var init_masters = __esm({
8782
+ "src/modules/masters/index.ts"() {
8783
+ "use strict";
8784
+ init_definitions();
8785
+ init_registry2();
8786
+ init_constants();
8787
+ init_install_type();
8788
+ init_uninstall_type();
8789
+ init_definitions();
8790
+ init_registry2();
8791
+ init_constants();
8627
8792
  mastersModule = {
8628
8793
  name: "masters",
8629
8794
  type: "core",
8630
- label: { en: "Master Data", es: "Datos maestros" },
8795
+ label: { en: "Masters", es: "Maestros" },
8631
8796
  icon: "mdi:database-outline",
8632
8797
  description: { en: "Reference data catalogs", es: "Cat\xE1logos de datos de referencia" },
8633
- category: "data",
8798
+ category: "settings",
8634
8799
  routePrefix: "/masters",
8635
8800
  definitions: [mastersEntity],
8801
+ actions: [installTypeAction, uninstallTypeAction],
8636
8802
  init: (ctx) => {
8637
8803
  if (!ctx.services.has("masters")) {
8638
8804
  ctx.services.register("masters", createMasterRegistry());
@@ -8643,13 +8809,14 @@ var init_masters = __esm({
8643
8809
  ctx.services.register("masters", createMasterRegistry());
8644
8810
  }
8645
8811
  const registry2 = ctx.services.get("masters");
8646
- const configMasters = ctx.config.resolved["masters"];
8647
- const typesToSeed = !configMasters || configMasters === "*" ? Object.keys(PREDEFINED_MASTERS) : configMasters.filter((t) => t in PREDEFINED_MASTERS);
8648
- for (const type2 of typesToSeed) {
8649
- const loader = PREDEFINED_MASTERS[type2];
8650
- if (!loader) continue;
8651
- const entries = await loader();
8652
- registry2.register(type2, entries, { seed: "if-empty" });
8812
+ const existing = await ctx.db.knex("masters").first();
8813
+ if (!existing) {
8814
+ for (const type2 of DEFAULT_MASTER_TYPES) {
8815
+ const loader = PREDEFINED_MASTERS[type2];
8816
+ if (!loader) continue;
8817
+ const entries = await loader();
8818
+ registry2.register(type2, entries, { seed: "if-empty" });
8819
+ }
8653
8820
  }
8654
8821
  await registry2.seed(ctx);
8655
8822
  }
@@ -8913,6 +9080,7 @@ function toPageDTO(page) {
8913
9080
  dataSource: page.dataSource,
8914
9081
  widgets: page.widgets,
8915
9082
  component: page.component,
9083
+ contentEndpoint: page.contentEndpoint,
8916
9084
  meta: page.meta,
8917
9085
  layout: page.layout
8918
9086
  };
@@ -9176,7 +9344,7 @@ var init_system_routes = __esm({
9176
9344
 
9177
9345
  // src/modules/system/system.entity.ts
9178
9346
  import * as os from "os";
9179
- import { useIconField, useTextField as useTextField3, useSelectField as useSelectField2, useNumberField as useNumberField2, useCheckboxField, useTagsField, useNameField, useDescriptionField } from "@gzl10/nexus-sdk/fields";
9347
+ import { useIconField, useTextField as useTextField4, useSelectField as useSelectField3, useNumberField as useNumberField2, useCheckboxField, useTagsField, useNameField, useDescriptionField } from "@gzl10/nexus-sdk/fields";
9180
9348
  var moduleEntity, osEntity;
9181
9349
  var init_system_entity = __esm({
9182
9350
  "src/modules/system/system.entity.ts"() {
@@ -9194,7 +9362,7 @@ var init_system_entity = __esm({
9194
9362
  name: useNameField({
9195
9363
  size: 50
9196
9364
  }),
9197
- label: useTextField3({
9365
+ label: useTextField4({
9198
9366
  label: { en: "Label", es: "Etiqueta" },
9199
9367
  size: 100,
9200
9368
  nullable: false,
@@ -9202,7 +9370,7 @@ var init_system_entity = __esm({
9202
9370
  }),
9203
9371
  icon: useIconField({ label: { en: "Icon", es: "Icono" }, size: 50 }),
9204
9372
  type: {
9205
- ...useSelectField2({
9373
+ ...useSelectField3({
9206
9374
  label: { en: "Type", es: "Tipo" },
9207
9375
  options: [
9208
9376
  { value: "core", label: { en: "Core", es: "Core" } },
@@ -9218,7 +9386,7 @@ var init_system_entity = __esm({
9218
9386
  description: useDescriptionField({
9219
9387
  mode: "text"
9220
9388
  }),
9221
- routePrefix: useTextField3({
9389
+ routePrefix: useTextField4({
9222
9390
  label: { en: "Route", es: "Ruta" },
9223
9391
  size: 50,
9224
9392
  nullable: true
@@ -9276,14 +9444,14 @@ var init_system_entity = __esm({
9276
9444
  order: 1,
9277
9445
  refreshInterval: 5e3,
9278
9446
  fields: {
9279
- hostname: useTextField3({
9447
+ hostname: useTextField4({
9280
9448
  label: { en: "Hostname", es: "Nombre de servidor" },
9281
9449
  size: 100,
9282
9450
  nullable: false,
9283
9451
  inputProps: { order: 1 }
9284
9452
  }),
9285
9453
  platform: {
9286
- ...useSelectField2({
9454
+ ...useSelectField3({
9287
9455
  label: { en: "Platform", es: "Plataforma" },
9288
9456
  options: [
9289
9457
  { value: "darwin", label: { en: "macOS", es: "macOS" } },
@@ -9297,7 +9465,7 @@ var init_system_entity = __esm({
9297
9465
  inputProps: { order: 2 }
9298
9466
  },
9299
9467
  arch: {
9300
- ...useSelectField2({
9468
+ ...useSelectField3({
9301
9469
  label: { en: "Architecture", es: "Arquitectura" },
9302
9470
  options: [
9303
9471
  { value: "x64", label: { en: "x64", es: "x64" } },
@@ -9310,7 +9478,7 @@ var init_system_entity = __esm({
9310
9478
  inputProps: { order: 3 }
9311
9479
  },
9312
9480
  type: {
9313
- ...useTextField3({
9481
+ ...useTextField4({
9314
9482
  label: { en: "Type", es: "Tipo" },
9315
9483
  size: 30,
9316
9484
  nullable: false
@@ -9318,7 +9486,7 @@ var init_system_entity = __esm({
9318
9486
  inputProps: { order: 4 }
9319
9487
  },
9320
9488
  release: {
9321
- ...useTextField3({
9489
+ ...useTextField4({
9322
9490
  label: { en: "Release", es: "Versi\xF3n" },
9323
9491
  size: 50,
9324
9492
  nullable: false
@@ -9341,7 +9509,7 @@ var init_system_entity = __esm({
9341
9509
  inputProps: { order: 7, format: "duration" }
9342
9510
  },
9343
9511
  cpuModel: {
9344
- ...useTextField3({
9512
+ ...useTextField4({
9345
9513
  label: { en: "CPU Model", es: "Modelo de CPU" },
9346
9514
  size: 100,
9347
9515
  nullable: false
@@ -9407,7 +9575,7 @@ var init_system_entity = __esm({
9407
9575
  });
9408
9576
 
9409
9577
  // src/modules/system/migration-history.entity.ts
9410
- import { useIdField as useIdField2, useTextField as useTextField4, useNumberField as useNumberField3, useSelectField as useSelectField3, useDatetimeField as useDatetimeField2, useTextareaField as useTextareaField2 } from "@gzl10/nexus-sdk/fields";
9578
+ import { useIdField as useIdField2, useTextField as useTextField5, useNumberField as useNumberField3, useSelectField as useSelectField4, useDatetimeField as useDatetimeField2, useTextareaField as useTextareaField2 } from "@gzl10/nexus-sdk/fields";
9411
9579
  var migrationHistoryEntity;
9412
9580
  var init_migration_history_entity = __esm({
9413
9581
  "src/modules/system/migration-history.entity.ts"() {
@@ -9424,9 +9592,9 @@ var init_migration_history_entity = __esm({
9424
9592
  calendarFrom: "executed_at",
9425
9593
  fields: {
9426
9594
  id: useIdField2(),
9427
- name: useTextField4({ label: { en: "Migration Name", es: "Nombre de la Migraci\xF3n" }, required: true, size: 255, unique: true, meta: { sortable: true, searchable: true } }),
9595
+ name: useTextField5({ label: { en: "Migration Name", es: "Nombre de la Migraci\xF3n" }, required: true, size: 255, unique: true, meta: { sortable: true, searchable: true } }),
9428
9596
  batch: useNumberField3({ label: { en: "Batch", es: "Lote" }, required: true, meta: { sortable: true } }),
9429
- status: useSelectField3({
9597
+ status: useSelectField4({
9430
9598
  label: { en: "Status", es: "Estado" },
9431
9599
  options: [
9432
9600
  { value: "running", label: { en: "Running", es: "Ejecutando" } },
@@ -9458,7 +9626,7 @@ var init_migration_history_entity = __esm({
9458
9626
  });
9459
9627
 
9460
9628
  // src/modules/system/env-config.entity.ts
9461
- import { useTextField as useTextField5, useSelectField as useSelectField4, useCheckboxField as useCheckboxField2 } from "@gzl10/nexus-sdk/fields";
9629
+ import { useTextField as useTextField6, useSelectField as useSelectField5, useCheckboxField as useCheckboxField2 } from "@gzl10/nexus-sdk/fields";
9462
9630
  function maskValue(value, sensitive) {
9463
9631
  if (!sensitive) return value;
9464
9632
  try {
@@ -9483,14 +9651,14 @@ var init_env_config_entity = __esm({
9483
9651
  routePrefix: "/env-config",
9484
9652
  defaultSort: { field: "category", order: "asc" },
9485
9653
  fields: {
9486
- name: useTextField5({
9654
+ name: useTextField6({
9487
9655
  label: { en: "Variable", es: "Variable" },
9488
9656
  size: 100,
9489
9657
  nullable: false,
9490
9658
  meta: { sortable: true, searchable: true }
9491
9659
  }),
9492
9660
  category: {
9493
- ...useSelectField4({
9661
+ ...useSelectField5({
9494
9662
  label: { en: "Category", es: "Categor\xEDa" },
9495
9663
  options: [
9496
9664
  { value: "server", label: { en: "Server", es: "Servidor" } },
@@ -9505,18 +9673,18 @@ var init_env_config_entity = __esm({
9505
9673
  meta: { sortable: true }
9506
9674
  })
9507
9675
  },
9508
- source: useTextField5({
9676
+ source: useTextField6({
9509
9677
  label: { en: "Source", es: "Origen" },
9510
9678
  size: 50,
9511
9679
  nullable: false,
9512
9680
  meta: { sortable: true }
9513
9681
  }),
9514
- value: useTextField5({
9682
+ value: useTextField6({
9515
9683
  label: { en: "Value", es: "Valor" },
9516
9684
  size: 255,
9517
9685
  nullable: true
9518
9686
  }),
9519
- default: useTextField5({
9687
+ default: useTextField6({
9520
9688
  label: { en: "Default", es: "Por defecto" },
9521
9689
  size: 100,
9522
9690
  nullable: true
@@ -10222,7 +10390,7 @@ var init_system = __esm({
10222
10390
  });
10223
10391
 
10224
10392
  // src/modules/ui-settings/ui-branding.entity.ts
10225
- import { useTextField as useTextField6, useImageField } from "@gzl10/nexus-sdk/fields";
10393
+ import { useTextField as useTextField7, useImageField } from "@gzl10/nexus-sdk/fields";
10226
10394
  var uiBrandingEntity;
10227
10395
  var init_ui_branding_entity = __esm({
10228
10396
  "src/modules/ui-settings/ui-branding.entity.ts"() {
@@ -10242,7 +10410,7 @@ var init_ui_branding_entity = __esm({
10242
10410
  favicon: null
10243
10411
  },
10244
10412
  fields: {
10245
- appName: useTextField6({
10413
+ appName: useTextField7({
10246
10414
  label: { en: "App Name", es: "Nombre de la App" },
10247
10415
  hint: { en: "Displayed in the header, browser tab and emails", es: "Se muestra en el header, pesta\xF1a del navegador y emails" },
10248
10416
  size: 100,
@@ -10283,7 +10451,7 @@ var init_ui_branding_entity = __esm({
10283
10451
  });
10284
10452
 
10285
10453
  // src/modules/ui-settings/ui-theme.entity.ts
10286
- import { useSelectField as useSelectField5, useColorField } from "@gzl10/nexus-sdk/fields";
10454
+ import { useSelectField as useSelectField6, useColorField } from "@gzl10/nexus-sdk/fields";
10287
10455
  var uiThemeEntity;
10288
10456
  var init_ui_theme_entity = __esm({
10289
10457
  "src/modules/ui-settings/ui-theme.entity.ts"() {
@@ -10308,7 +10476,7 @@ var init_ui_theme_entity = __esm({
10308
10476
  },
10309
10477
  fields: {
10310
10478
  // === Typography ===
10311
- font: useSelectField5({
10479
+ font: useSelectField6({
10312
10480
  label: { en: "Font", es: "Fuente" },
10313
10481
  hint: { en: "Primary font for headings and UI elements", es: "Fuente principal para t\xEDtulos y elementos de interfaz" },
10314
10482
  options: [
@@ -10321,7 +10489,7 @@ var init_ui_theme_entity = __esm({
10321
10489
  ]
10322
10490
  }),
10323
10491
  // === Theme & Colors ===
10324
- theme: useSelectField5({
10492
+ theme: useSelectField6({
10325
10493
  label: { en: "Theme", es: "Tema" },
10326
10494
  hint: { en: "System follows your device preferences", es: "Sistema sigue las preferencias de tu dispositivo" },
10327
10495
  options: [
@@ -10330,7 +10498,7 @@ var init_ui_theme_entity = __esm({
10330
10498
  { value: "system", label: { en: "System", es: "Sistema" } }
10331
10499
  ]
10332
10500
  }),
10333
- dopamineTheme: useSelectField5({
10501
+ dopamineTheme: useSelectField6({
10334
10502
  label: { en: "Dopamine Theme", es: "Tema Dopamina" },
10335
10503
  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)" },
10336
10504
  options: [
@@ -10348,7 +10516,7 @@ var init_ui_theme_entity = __esm({
10348
10516
  ]
10349
10517
  }),
10350
10518
  // === Login Layout ===
10351
- loginLayout: useSelectField5({
10519
+ loginLayout: useSelectField6({
10352
10520
  label: { en: "Login Layout", es: "Dise\xF1o de Login" },
10353
10521
  hint: { en: "Visual layout for authentication pages", es: "Dise\xF1o visual para p\xE1ginas de autenticaci\xF3n" },
10354
10522
  options: [
@@ -10379,7 +10547,7 @@ var init_ui_theme_entity = __esm({
10379
10547
  });
10380
10548
 
10381
10549
  // src/modules/ui-settings/ui-effects.entity.ts
10382
- import { useSelectField as useSelectField6, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
10550
+ import { useSelectField as useSelectField7, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
10383
10551
  var uiEffectsEntity;
10384
10552
  var init_ui_effects_entity = __esm({
10385
10553
  "src/modules/ui-settings/ui-effects.entity.ts"() {
@@ -10399,7 +10567,7 @@ var init_ui_effects_entity = __esm({
10399
10567
  enableOrganicShapes: false
10400
10568
  },
10401
10569
  fields: {
10402
- glassIntensity: useSelectField6({
10570
+ glassIntensity: useSelectField7({
10403
10571
  label: { en: "Glass Intensity", es: "Intensidad Glass" },
10404
10572
  hint: { en: "Glassmorphism blur effect on cards and modals", es: "Efecto de desenfoque glassmorphism en cards y modales" },
10405
10573
  options: [
@@ -10409,7 +10577,7 @@ var init_ui_effects_entity = __esm({
10409
10577
  { value: "high", label: { en: "High", es: "Alta" } }
10410
10578
  ]
10411
10579
  }),
10412
- borderStyle: useSelectField6({
10580
+ borderStyle: useSelectField7({
10413
10581
  label: { en: "Border Style", es: "Estilo de Bordes" },
10414
10582
  hint: { en: "Corner radius for buttons, cards and inputs", es: "Radio de esquinas para botones, cards e inputs" },
10415
10583
  options: [
@@ -10443,7 +10611,7 @@ var init_ui_effects_entity = __esm({
10443
10611
  });
10444
10612
 
10445
10613
  // src/modules/ui-settings/ui-accessibility.entity.ts
10446
- import { useSelectField as useSelectField7, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
10614
+ import { useSelectField as useSelectField8, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
10447
10615
  var uiAccessibilityEntity;
10448
10616
  var init_ui_accessibility_entity = __esm({
10449
10617
  "src/modules/ui-settings/ui-accessibility.entity.ts"() {
@@ -10462,7 +10630,7 @@ var init_ui_accessibility_entity = __esm({
10462
10630
  highContrast: false
10463
10631
  },
10464
10632
  fields: {
10465
- typographyScale: useSelectField7({
10633
+ typographyScale: useSelectField8({
10466
10634
  label: { en: "Typography Scale", es: "Escala Tipogr\xE1fica" },
10467
10635
  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)" },
10468
10636
  options: [
@@ -11453,7 +11621,7 @@ var init_storage_service = __esm({
11453
11621
  });
11454
11622
 
11455
11623
  // src/modules/storage/storage.entity.ts
11456
- import { useIdField as useIdField3, useTextField as useTextField7, useSelectField as useSelectField8, useUrlField, useNumberField as useNumberField4, useCheckboxField as useCheckboxField3, useJsonField as useJsonField2, useMetadataField, usePublicField } from "@gzl10/nexus-sdk/fields";
11624
+ 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";
11457
11625
  var DEFAULT_MAX_SIZE2, storageConfigEntity, storageFilesEntity;
11458
11626
  var init_storage_entity = __esm({
11459
11627
  "src/modules/storage/storage.entity.ts"() {
@@ -11479,7 +11647,7 @@ var init_storage_entity = __esm({
11479
11647
  },
11480
11648
  fields: {
11481
11649
  id: useIdField3(),
11482
- scope: useTextField7({
11650
+ scope: useTextField8({
11483
11651
  label: { en: "Scope", es: "\xC1mbito" },
11484
11652
  disabled: true,
11485
11653
  size: 100,
@@ -11488,7 +11656,7 @@ var init_storage_entity = __esm({
11488
11656
  hint: { en: "Unique identifier (e.g. default_filesystem, default_s3)", es: "Identificador \xFAnico (ej: default_filesystem, default_s3)" }
11489
11657
  }),
11490
11658
  driver: {
11491
- ...useSelectField8({
11659
+ ...useSelectField9({
11492
11660
  label: { en: "Driver", es: "Controlador" },
11493
11661
  required: true,
11494
11662
  hint: { en: "Storage backend", es: "Backend de almacenamiento" },
@@ -11511,7 +11679,7 @@ var init_storage_entity = __esm({
11511
11679
  nullable: false,
11512
11680
  displayProps: { format: "bytes" }
11513
11681
  }),
11514
- allowed_mime_types: useTextField7({
11682
+ allowed_mime_types: useTextField8({
11515
11683
  label: { en: "Allowed MIME Types", es: "Tipos MIME permitidos" },
11516
11684
  hint: { en: "Allowed types separated by comma (e.g. image/*,application/pdf)", es: "Tipos permitidos separados por coma (ej: image/*,application/pdf)" },
11517
11685
  size: 1e3,
@@ -11676,21 +11844,21 @@ var init_storage_entity = __esm({
11676
11844
  ],
11677
11845
  fields: {
11678
11846
  id: useIdField3(),
11679
- filename: useTextField7({
11847
+ filename: useTextField8({
11680
11848
  label: { en: "Original Filename", es: "Nombre original" },
11681
11849
  required: true,
11682
11850
  size: 255,
11683
11851
  nullable: false,
11684
11852
  meta: { sortable: true, searchable: true }
11685
11853
  }),
11686
- disk_filename: useTextField7({
11854
+ disk_filename: useTextField8({
11687
11855
  label: { en: "Disk Filename", es: "Nombre en disco" },
11688
11856
  required: true,
11689
11857
  hidden: true,
11690
11858
  size: 255,
11691
11859
  nullable: false
11692
11860
  }),
11693
- mimetype: useTextField7({
11861
+ mimetype: useTextField8({
11694
11862
  label: { en: "MIME Type", es: "Tipo MIME" },
11695
11863
  required: true,
11696
11864
  size: 100,
@@ -11706,14 +11874,14 @@ var init_storage_entity = __esm({
11706
11874
  meta: { sortable: true },
11707
11875
  displayProps: { format: "bytes" }
11708
11876
  }),
11709
- folder: useTextField7({
11877
+ folder: useTextField8({
11710
11878
  label: { en: "Folder", es: "Carpeta" },
11711
11879
  size: 100,
11712
11880
  nullable: true,
11713
11881
  index: true,
11714
11882
  meta: { searchable: true }
11715
11883
  }),
11716
- scope: useTextField7({
11884
+ scope: useTextField8({
11717
11885
  label: { en: "Storage Scope", es: "\xC1mbito de almacenamiento" },
11718
11886
  hidden: true,
11719
11887
  size: 50,
@@ -11724,7 +11892,7 @@ var init_storage_entity = __esm({
11724
11892
  es: "\xC1mbito de configuraci\xF3n de almacenamiento usado para este archivo"
11725
11893
  }
11726
11894
  }),
11727
- path: useTextField7({
11895
+ path: useTextField8({
11728
11896
  label: { en: "Full Path", es: "Ruta completa" },
11729
11897
  required: true,
11730
11898
  hidden: true,
@@ -11737,13 +11905,13 @@ var init_storage_entity = __esm({
11737
11905
  nullable: true,
11738
11906
  meta: { exportable: true, showInDisplay: false }
11739
11907
  }),
11740
- thumbnail_path: useTextField7({
11908
+ thumbnail_path: useTextField8({
11741
11909
  label: { en: "Thumbnail Path", es: "Ruta de miniatura" },
11742
11910
  size: 500,
11743
11911
  nullable: true,
11744
11912
  meta: { showInDisplay: false }
11745
11913
  }),
11746
- hash: useTextField7({
11914
+ hash: useTextField8({
11747
11915
  label: { en: "SHA256 Hash", es: "Hash SHA256" },
11748
11916
  hidden: true,
11749
11917
  size: 64,
@@ -12240,7 +12408,7 @@ var init_storage = __esm({
12240
12408
  });
12241
12409
 
12242
12410
  // src/modules/users/users.entity.ts
12243
- import { useIdField as useIdField4, useSelectField as useSelectField9, useEmailField, usePasswordField, useTextField as useTextField8, useDatetimeField as useDatetimeField3, useCheckboxField as useCheckboxField4, useImageField as useImageField2, useNameField as useNameField2, useMetadataField as useMetadataField2, useDescriptionField as useDescriptionField2 } from "@gzl10/nexus-sdk/fields";
12411
+ 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";
12244
12412
  import { z as z2 } from "zod";
12245
12413
  var userEntity, roleEntity, userRoleEntity;
12246
12414
  var init_users_entity = __esm({
@@ -12303,7 +12471,7 @@ var init_users_entity = __esm({
12303
12471
  nullable: true,
12304
12472
  meta: { exportable: true, showInForm: false, showInDisplay: false }
12305
12473
  }),
12306
- consent_version: useTextField8({
12474
+ consent_version: useTextField9({
12307
12475
  label: { en: "Consent Version", es: "Versi\xF3n de consentimiento" },
12308
12476
  hidden: true,
12309
12477
  size: 20,
@@ -12314,7 +12482,7 @@ var init_users_entity = __esm({
12314
12482
  label: { en: "Marketing Opt-in", es: "Aceptar marketing" },
12315
12483
  meta: { exportable: true, showInForm: false, showInDisplay: false }
12316
12484
  }),
12317
- locale: useSelectField9({
12485
+ locale: useSelectField10({
12318
12486
  label: { en: "Language", es: "Idioma" },
12319
12487
  options: [
12320
12488
  { value: "es", label: { en: "Spanish", es: "Espa\xF1ol" } },
@@ -12324,13 +12492,13 @@ var init_users_entity = __esm({
12324
12492
  meta: { sortable: true },
12325
12493
  defaultValue: "en"
12326
12494
  }),
12327
- timezone: useSelectField9({
12495
+ timezone: useSelectField10({
12328
12496
  label: { en: "Timezone", es: "Zona horaria" },
12329
12497
  master: "timezones",
12330
12498
  meta: { sortable: true },
12331
12499
  defaultValue: "timezones:Europe/Madrid"
12332
12500
  }),
12333
- type: useSelectField9({
12501
+ type: useSelectField10({
12334
12502
  label: { en: "Type", es: "Tipo" },
12335
12503
  defaultValue: "human",
12336
12504
  options: [
@@ -12499,7 +12667,7 @@ var init_users_entity = __esm({
12499
12667
  expose: false,
12500
12668
  fields: {
12501
12669
  id: useIdField4(),
12502
- user_id: useSelectField9({
12670
+ user_id: useSelectField10({
12503
12671
  label: { en: "User", es: "Usuario" },
12504
12672
  required: true,
12505
12673
  table: "users",
@@ -12510,7 +12678,7 @@ var init_users_entity = __esm({
12510
12678
  labelField: "name",
12511
12679
  meta: { searchable: true }
12512
12680
  }),
12513
- role_id: useSelectField9({
12681
+ role_id: useSelectField10({
12514
12682
  label: { en: "Role", es: "Rol" },
12515
12683
  required: true,
12516
12684
  table: "roles",
@@ -13454,7 +13622,7 @@ var init_users = __esm({
13454
13622
  });
13455
13623
 
13456
13624
  // src/modules/auth/auth.entity.ts
13457
- import { useIdField as useIdField5, useTextField as useTextField9, useSelectField as useSelectField10, useDatetimeField as useDatetimeField4, useEmailField as useEmailField2, useMetadataField as useMetadataField3, useExpiresAtField } from "@gzl10/nexus-sdk/fields";
13625
+ 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";
13458
13626
  var refreshTokenEntity, authIdentitiesEntity;
13459
13627
  var init_auth_entity = __esm({
13460
13628
  "src/modules/auth/auth.entity.ts"() {
@@ -13469,7 +13637,7 @@ var init_auth_entity = __esm({
13469
13637
  expose: false,
13470
13638
  fields: {
13471
13639
  id: useIdField5(),
13472
- token: useTextField9({
13640
+ token: useTextField10({
13473
13641
  label: { en: "Token", es: "Token" },
13474
13642
  hidden: true,
13475
13643
  size: 255,
@@ -13496,14 +13664,14 @@ var init_auth_entity = __esm({
13496
13664
  nullable: true,
13497
13665
  meta: { sortable: true }
13498
13666
  }),
13499
- device_id: useTextField9({
13667
+ device_id: useTextField10({
13500
13668
  label: { en: "Device ID", es: "ID de dispositivo" },
13501
13669
  size: 64,
13502
13670
  index: true,
13503
13671
  nullable: true,
13504
13672
  meta: { searchable: true }
13505
13673
  }),
13506
- device_name: useTextField9({
13674
+ device_name: useTextField10({
13507
13675
  label: { en: "Device", es: "Dispositivo" },
13508
13676
  size: 100,
13509
13677
  nullable: true,
@@ -13535,7 +13703,7 @@ var init_auth_entity = __esm({
13535
13703
  order: 5,
13536
13704
  fields: {
13537
13705
  id: useIdField5(),
13538
- user_id: useSelectField10({
13706
+ user_id: useSelectField11({
13539
13707
  label: { en: "User", es: "Usuario" },
13540
13708
  table: "users",
13541
13709
  column: "id",
@@ -13547,7 +13715,7 @@ var init_auth_entity = __esm({
13547
13715
  labelField: "name",
13548
13716
  meta: { searchable: true }
13549
13717
  }),
13550
- provider: useTextField9({
13718
+ provider: useTextField10({
13551
13719
  label: { en: "Provider", es: "Proveedor" },
13552
13720
  required: true,
13553
13721
  size: 50,
@@ -13556,7 +13724,7 @@ var init_auth_entity = __esm({
13556
13724
  hint: { en: "e.g. pocketid, google, microsoft", es: "ej. pocketid, google, microsoft" },
13557
13725
  meta: { sortable: true, searchable: true }
13558
13726
  }),
13559
- provider_user_id: useTextField9({
13727
+ provider_user_id: useTextField10({
13560
13728
  label: { en: "Provider User ID", es: "ID de usuario del proveedor" },
13561
13729
  required: true,
13562
13730
  size: 255,
@@ -13851,7 +14019,7 @@ var init_auth_middleware = __esm({
13851
14019
  });
13852
14020
 
13853
14021
  // src/modules/auth/auth.pat.entity.ts
13854
- import { useIdField as useIdField6, useTextField as useTextField10, useSelectField as useSelectField11, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
14022
+ import { useIdField as useIdField6, useTextField as useTextField11, useSelectField as useSelectField12, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
13855
14023
  var personalTokenEntity;
13856
14024
  var init_auth_pat_entity = __esm({
13857
14025
  "src/modules/auth/auth.pat.entity.ts"() {
@@ -13868,7 +14036,7 @@ var init_auth_pat_entity = __esm({
13868
14036
  routePrefix: "/personal-tokens",
13869
14037
  fields: {
13870
14038
  id: useIdField6(),
13871
- user_id: useSelectField11({
14039
+ user_id: useSelectField12({
13872
14040
  label: { en: "User", es: "Usuario" },
13873
14041
  table: "users",
13874
14042
  column: "id",
@@ -13880,7 +14048,7 @@ var init_auth_pat_entity = __esm({
13880
14048
  labelField: "name",
13881
14049
  meta: { searchable: true }
13882
14050
  }),
13883
- name: useTextField10({
14051
+ name: useTextField11({
13884
14052
  label: { en: "Name", es: "Nombre" },
13885
14053
  required: true,
13886
14054
  size: 100,
@@ -13888,14 +14056,14 @@ var init_auth_pat_entity = __esm({
13888
14056
  hint: { en: "Descriptive name for this token", es: "Nombre descriptivo para este token" },
13889
14057
  meta: { searchable: true }
13890
14058
  }),
13891
- token_prefix: useTextField10({
14059
+ token_prefix: useTextField11({
13892
14060
  label: { en: "Token", es: "Token" },
13893
14061
  size: 20,
13894
14062
  disabled: true,
13895
14063
  nullable: false,
13896
14064
  hint: { en: "Partial token for identification", es: "Token parcial para identificaci\xF3n" }
13897
14065
  }),
13898
- token_hash: useTextField10({
14066
+ token_hash: useTextField11({
13899
14067
  label: { en: "Token Hash", es: "Hash del token" },
13900
14068
  size: 64,
13901
14069
  hidden: true,
@@ -13903,7 +14071,7 @@ var init_auth_pat_entity = __esm({
13903
14071
  unique: true,
13904
14072
  meta: { exportable: false }
13905
14073
  }),
13906
- scope: useSelectField11({
14074
+ scope: useSelectField12({
13907
14075
  label: { en: "Permission", es: "Permiso" },
13908
14076
  required: true,
13909
14077
  options: [
@@ -15659,7 +15827,7 @@ var init_mail_service = __esm({
15659
15827
  });
15660
15828
 
15661
15829
  // src/modules/mail/mail.entity.ts
15662
- import { useIdField as useIdField7, useTextField as useTextField11, useSelectField as useSelectField12, 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";
15830
+ 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";
15663
15831
  import nodemailer2 from "nodemailer";
15664
15832
  async function getMailConfigFromDB(ctx) {
15665
15833
  const configService = ctx.services["config"];
@@ -15722,7 +15890,7 @@ var init_mail_entity = __esm({
15722
15890
  },
15723
15891
  fields: {
15724
15892
  id: useIdField7(),
15725
- host: useTextField11({
15893
+ host: useTextField12({
15726
15894
  label: { en: "SMTP Host", es: "Host SMTP" },
15727
15895
  size: 255,
15728
15896
  nullable: false,
@@ -15742,7 +15910,7 @@ var init_mail_entity = __esm({
15742
15910
  nullable: false,
15743
15911
  hint: { en: "Default: SMTP_FROM env var", es: "Por defecto: variable SMTP_FROM" }
15744
15912
  }),
15745
- auth_user: useTextField11({
15913
+ auth_user: useTextField12({
15746
15914
  label: { en: "Auth User", es: "Usuario de autenticaci\xF3n" },
15747
15915
  size: 255,
15748
15916
  nullable: true,
@@ -15774,12 +15942,12 @@ var init_mail_entity = __esm({
15774
15942
  required: true,
15775
15943
  validation: { format: "email" }
15776
15944
  }),
15777
- subject: useTextField11({
15945
+ subject: useTextField12({
15778
15946
  label: { en: "Subject", es: "Asunto" },
15779
15947
  required: true,
15780
15948
  validation: { min: 1, max: 255 }
15781
15949
  }),
15782
- title: useTextField11({
15950
+ title: useTextField12({
15783
15951
  label: { en: "Title", es: "T\xEDtulo" },
15784
15952
  hint: { en: "Large title in email header", es: "T\xEDtulo grande en la cabecera del correo" }
15785
15953
  }),
@@ -15923,20 +16091,20 @@ var init_mail_entity = __esm({
15923
16091
  nullable: false,
15924
16092
  meta: { sortable: true }
15925
16093
  }),
15926
- to: useTextField11({
16094
+ to: useTextField12({
15927
16095
  label: { en: "Recipient(s)", es: "Destinatario(s)" },
15928
16096
  size: 1e3,
15929
16097
  nullable: false,
15930
16098
  meta: { searchable: true }
15931
16099
  }),
15932
- subject: useTextField11({
16100
+ subject: useTextField12({
15933
16101
  label: { en: "Subject", es: "Asunto" },
15934
16102
  size: 255,
15935
16103
  nullable: false,
15936
16104
  meta: { searchable: true, sortable: true }
15937
16105
  }),
15938
16106
  status: {
15939
- ...useSelectField12({
16107
+ ...useSelectField13({
15940
16108
  label: { en: "Status", es: "Estado" },
15941
16109
  options: [
15942
16110
  { value: "pending", label: { en: "Pending", es: "Pendiente" } },
@@ -15950,7 +16118,7 @@ var init_mail_entity = __esm({
15950
16118
  }),
15951
16119
  validation: { enum: ["pending", "sent", "failed", "bounced"] }
15952
16120
  },
15953
- message_id: useTextField11({
16121
+ message_id: useTextField12({
15954
16122
  label: { en: "Message ID", es: "ID de mensaje" },
15955
16123
  hidden: true,
15956
16124
  size: 255,
@@ -15960,7 +16128,7 @@ var init_mail_entity = __esm({
15960
16128
  label: { en: "Error", es: "Error" },
15961
16129
  nullable: true
15962
16130
  }),
15963
- sent_by: useSelectField12({
16131
+ sent_by: useSelectField13({
15964
16132
  label: { en: "Sent by", es: "Enviado por" },
15965
16133
  table: "users",
15966
16134
  column: "id",
@@ -16595,7 +16763,7 @@ var init_toggle_plugin_action = __esm({
16595
16763
  });
16596
16764
 
16597
16765
  // src/modules/plugins/plugins.entity.ts
16598
- import { useTextField as useTextField12, useSelectField as useSelectField13, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16766
+ import { useTextField as useTextField13, useSelectField as useSelectField14, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16599
16767
  import { OFFICIAL_PLUGINS } from "@gzl10/nexus-sdk";
16600
16768
  var allowPluginManagement, pluginsEntity;
16601
16769
  var init_plugins_entity = __esm({
@@ -16614,30 +16782,30 @@ var init_plugins_entity = __esm({
16614
16782
  routePrefix: "/",
16615
16783
  defaultSort: { field: "name", order: "asc" },
16616
16784
  fields: {
16617
- name: useTextField12({
16785
+ name: useTextField13({
16618
16786
  label: { en: "Name", es: "Nombre" },
16619
16787
  size: 50,
16620
16788
  nullable: false,
16621
16789
  meta: { sortable: true, searchable: true }
16622
16790
  }),
16623
- code: useTextField12({
16791
+ code: useTextField13({
16624
16792
  label: { en: "Code", es: "C\xF3digo" },
16625
16793
  size: 10,
16626
16794
  nullable: false,
16627
16795
  meta: { sortable: true }
16628
16796
  }),
16629
- label: useTextField12({
16797
+ label: useTextField13({
16630
16798
  label: { en: "Label", es: "Etiqueta" },
16631
16799
  size: 100,
16632
16800
  nullable: false,
16633
16801
  meta: { sortable: true }
16634
16802
  }),
16635
- version: useTextField12({
16803
+ version: useTextField13({
16636
16804
  label: { en: "Version", es: "Versi\xF3n" },
16637
16805
  size: 20,
16638
16806
  nullable: false
16639
16807
  }),
16640
- category: useSelectField13({
16808
+ category: useSelectField14({
16641
16809
  label: { en: "Category", es: "Categor\xEDa" },
16642
16810
  options: [
16643
16811
  { value: "content", label: { en: "Content", es: "Contenido" } },
@@ -16778,8 +16946,8 @@ var init_plugins = __esm({
16778
16946
  // src/modules/audit/audit.entity.ts
16779
16947
  import {
16780
16948
  useIdField as useIdField8,
16781
- useTextField as useTextField13,
16782
- useSelectField as useSelectField14,
16949
+ useTextField as useTextField14,
16950
+ useSelectField as useSelectField15,
16783
16951
  useTextareaField as useTextareaField4,
16784
16952
  useJsonField as useJsonField3,
16785
16953
  useDatetimeField as useDatetimeField7,
@@ -16802,7 +16970,7 @@ var init_audit_entity = __esm({
16802
16970
  fields: {
16803
16971
  id: useIdField8(),
16804
16972
  source: {
16805
- ...useTextField13({
16973
+ ...useTextField14({
16806
16974
  label: { en: "Source", es: "Origen" },
16807
16975
  size: 100,
16808
16976
  nullable: false,
@@ -16812,7 +16980,7 @@ var init_audit_entity = __esm({
16812
16980
  validation: { min: 1, max: 100 }
16813
16981
  },
16814
16982
  action: {
16815
- ...useTextField13({
16983
+ ...useTextField14({
16816
16984
  label: { en: "Action", es: "Acci\xF3n" },
16817
16985
  size: 100,
16818
16986
  nullable: false,
@@ -16821,7 +16989,7 @@ var init_audit_entity = __esm({
16821
16989
  }),
16822
16990
  validation: { min: 1, max: 100 }
16823
16991
  },
16824
- actor_id: useSelectField14({
16992
+ actor_id: useSelectField15({
16825
16993
  label: { en: "Actor", es: "Actor" },
16826
16994
  table: "users",
16827
16995
  column: "id",
@@ -16838,20 +17006,20 @@ var init_audit_entity = __esm({
16838
17006
  nullable: true,
16839
17007
  meta: { searchable: true }
16840
17008
  }),
16841
- resource_type: useTextField13({
17009
+ resource_type: useTextField14({
16842
17010
  label: { en: "Resource Type", es: "Tipo de recurso" },
16843
17011
  size: 100,
16844
17012
  nullable: true,
16845
17013
  index: true,
16846
17014
  meta: { searchable: true }
16847
17015
  }),
16848
- resource_id: useTextField13({
17016
+ resource_id: useTextField14({
16849
17017
  label: { en: "Resource ID", es: "ID del recurso" },
16850
17018
  size: 100,
16851
17019
  nullable: true,
16852
17020
  meta: { searchable: true }
16853
17021
  }),
16854
- ip_address: useTextField13({
17022
+ ip_address: useTextField14({
16855
17023
  label: { en: "IP Address", es: "Direcci\xF3n IP" },
16856
17024
  size: 45,
16857
17025
  nullable: true,
@@ -17618,6 +17786,7 @@ var init_error_codes = __esm({
17618
17786
  DB_CONSTRAINT_UNIQUE: "DB_CONSTRAINT_UNIQUE",
17619
17787
  DB_CONSTRAINT_FK: "DB_CONSTRAINT_FK",
17620
17788
  DB_CONNECTION_ERROR: "DB_CONNECTION_ERROR",
17789
+ DATABASE_NOT_READY: "DATABASE_NOT_READY",
17621
17790
  // System
17622
17791
  SYSTEM_INTERNAL_ERROR: "SYSTEM_INTERNAL_ERROR"
17623
17792
  };
@@ -17941,7 +18110,7 @@ var init_env = __esm({
17941
18110
  envSchema = z8.object({
17942
18111
  NODE_ENV: z8.enum(["development", "production", "test"]).default("development"),
17943
18112
  PORT: z8.coerce.number().default(3e3),
17944
- CORS_ORIGIN: z8.string().default("http://localhost:3001"),
18113
+ CORS_ORIGIN: z8.string().default("*"),
17945
18114
  BACKEND_URL: z8.string().optional(),
17946
18115
  DATABASE_URL: z8.string().default("file:./dev.db"),
17947
18116
  REDIS_URL: z8.string().url().optional(),
@@ -20210,10 +20379,31 @@ var init_base_service = __esm({
20210
20379
  * Override in subclasses for entity-specific search
20211
20380
  */
20212
20381
  applySearch(qb, search) {
20382
+ const searchTerm = `%${search}%`;
20383
+ const searchableFields = [];
20384
+ const fields = "fields" in this.definition ? this.definition.fields : {};
20385
+ for (const [name, field] of Object.entries(fields)) {
20386
+ if (field.meta?.searchable) {
20387
+ searchableFields.push(name);
20388
+ }
20389
+ }
20213
20390
  if ("labelField" in this.definition && this.definition.labelField) {
20214
- const labelField = this.definition.labelField;
20215
- qb.where(labelField, "like", `%${search}%`);
20391
+ const lf = this.definition.labelField;
20392
+ if (!searchableFields.includes(lf)) {
20393
+ searchableFields.unshift(lf);
20394
+ }
20216
20395
  }
20396
+ if (searchableFields.length === 0) return qb;
20397
+ qb.where(function() {
20398
+ for (const fieldName of searchableFields) {
20399
+ const field = fields[fieldName];
20400
+ if (field?.db?.type === "json") {
20401
+ this.orWhereRaw(`CAST(?? AS TEXT) LIKE ?`, [fieldName, searchTerm]);
20402
+ } else {
20403
+ this.orWhere(fieldName, "like", searchTerm);
20404
+ }
20405
+ }
20406
+ });
20217
20407
  return qb;
20218
20408
  }
20219
20409
  /**
@@ -23687,15 +23877,6 @@ var init_ensure_system_tables = __esm({
23687
23877
  }
23688
23878
  });
23689
23879
 
23690
- // src/cli/shared.ts
23691
- import { consola } from "consola";
23692
- var init_shared = __esm({
23693
- "src/cli/shared.ts"() {
23694
- "use strict";
23695
- init_logger();
23696
- }
23697
- });
23698
-
23699
23880
  // src/db/migration-sources.ts
23700
23881
  function buildMigrationSources() {
23701
23882
  const sources = [
@@ -23715,9 +23896,7 @@ function buildMigrationSources() {
23715
23896
  var init_migration_sources = __esm({
23716
23897
  "src/db/migration-sources.ts"() {
23717
23898
  "use strict";
23718
- init_shared();
23719
23899
  init_paths();
23720
- init_plugin_ops();
23721
23900
  init_module_store();
23722
23901
  }
23723
23902
  });
@@ -24995,6 +25174,10 @@ function handleDbError(err) {
24995
25174
  { path: "foreignKey", message: "Foreign key constraint violation" }
24996
25175
  ]);
24997
25176
  }
25177
+ if (code === "42P01" || code === "ER_NO_SUCH_TABLE" || dbErr.errno === 1146 || msg.includes("no such table")) {
25178
+ logger.error({ err }, "Database table not found \u2014 database may need migration or was wiped");
25179
+ throw new AppError({ code: ErrorCodes.DATABASE_NOT_READY, message: "Database not ready" }, 503);
25180
+ }
24998
25181
  logger.error({ err }, "Unexpected database error");
24999
25182
  throw new AppError({ code: ErrorCodes.SYSTEM_INTERNAL_ERROR, message: "Internal database error" }, 500);
25000
25183
  }
@@ -27277,6 +27460,33 @@ async function setupModuleRoutes(app) {
27277
27460
  }
27278
27461
  }
27279
27462
  const caslRegistry = /* @__PURE__ */ new Map();
27463
+ function mergeActionPermissions(existing, incoming) {
27464
+ const merged = { ...existing };
27465
+ for (const [role, perm] of Object.entries(incoming)) {
27466
+ if (!merged[role]) {
27467
+ merged[role] = perm;
27468
+ } else {
27469
+ const existingArr = Array.isArray(merged[role]) ? merged[role] : [merged[role]];
27470
+ const incomingArr = Array.isArray(perm) ? perm : [perm];
27471
+ merged[role] = [...existingArr, ...incomingArr];
27472
+ }
27473
+ }
27474
+ return merged;
27475
+ }
27476
+ function registerActionCasl(action) {
27477
+ const casl = action.casl;
27478
+ if (!casl || !("subject" in casl) || !casl.subject || !("permissions" in casl) || !casl.permissions) return;
27479
+ const subject2 = casl.subject;
27480
+ const existing = caslRegistry.get(subject2);
27481
+ if (existing) {
27482
+ existing.permissions = mergeActionPermissions(existing.permissions, casl.permissions);
27483
+ } else {
27484
+ caslRegistry.set(subject2, {
27485
+ subject: subject2,
27486
+ permissions: casl.permissions ?? {}
27487
+ });
27488
+ }
27489
+ }
27280
27490
  for (const mod of modules) {
27281
27491
  for (const def of mod.definitions ?? []) {
27282
27492
  const casl = def.casl;
@@ -27286,6 +27496,12 @@ async function setupModuleRoutes(app) {
27286
27496
  subject: casl.subject,
27287
27497
  permissions: casl.permissions ?? {}
27288
27498
  });
27499
+ for (const action of def.actions ?? []) {
27500
+ registerActionCasl(action);
27501
+ }
27502
+ }
27503
+ for (const action of mod.actions ?? []) {
27504
+ registerActionCasl(action);
27289
27505
  }
27290
27506
  }
27291
27507
  setEntityDefinitions(caslRegistry);
@@ -27670,7 +27886,7 @@ function isFrpcInstalled() {
27670
27886
  return false;
27671
27887
  }
27672
27888
  }
27673
- function startTunnel(config3) {
27889
+ async function startTunnel(config3) {
27674
27890
  if (tunnelProcess) return true;
27675
27891
  if (!isFrpcInstalled()) {
27676
27892
  logger.warn("frpc binary not found \u2014 tunnel disabled. Install with: brew install frp");
@@ -27682,24 +27898,49 @@ function startTunnel(config3) {
27682
27898
  tunnelProcess = spawn("frpc", ["-c", tmpConfigPath], {
27683
27899
  stdio: ["ignore", "pipe", "pipe"]
27684
27900
  });
27685
- tunnelProcess.stdout?.on("data", (data) => {
27686
- const msg = data.toString().trim();
27687
- if (msg) logger.debug({ component: "tunnel" }, msg);
27688
- });
27689
- tunnelProcess.stderr?.on("data", (data) => {
27690
- const msg = data.toString().trim();
27691
- if (msg) logger.warn({ component: "tunnel" }, msg);
27692
- });
27693
- tunnelProcess.on("exit", (code) => {
27694
- if (code !== 0 && code !== null) {
27695
- logger.warn({ code, component: "tunnel" }, "frpc exited unexpectedly");
27696
- }
27697
- tunnelProcess = null;
27698
- cleanupConfig();
27901
+ outputBuffer = "";
27902
+ const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
27903
+ return new Promise((resolve2) => {
27904
+ let settled = false;
27905
+ const collectOutput = (data) => {
27906
+ const msg = stripAnsi(data.toString()).trim();
27907
+ if (!msg) return;
27908
+ outputBuffer += msg + "\n";
27909
+ logger.debug({ component: "tunnel" }, msg);
27910
+ if (!settled && msg.includes("start proxy success")) {
27911
+ settled = true;
27912
+ const url = getTunnelUrl(config3.subdomain, config3.server);
27913
+ logger.info({ url, component: "tunnel" }, `Tunnel active \u2192 ${url}`);
27914
+ resolve2(true);
27915
+ }
27916
+ };
27917
+ tunnelProcess.stdout?.on("data", collectOutput);
27918
+ tunnelProcess.stderr?.on("data", collectOutput);
27919
+ tunnelProcess.on("exit", (code) => {
27920
+ if (code !== 0 && code !== null) {
27921
+ const output = outputBuffer.trim();
27922
+ const hint = output.includes("authorization failed") || output.includes("auth failed") || output.includes("invalid token") ? " \u2014 check FRPC_TOKEN is correct" : output.includes("login to the server failed") ? ` \u2014 ${output.split("\n").pop()}` : "";
27923
+ logger.error(
27924
+ { code, reason: output || void 0, component: "tunnel" },
27925
+ `frpc failed to start${hint}`
27926
+ );
27927
+ }
27928
+ tunnelProcess = null;
27929
+ outputBuffer = "";
27930
+ cleanupConfig();
27931
+ if (!settled) {
27932
+ settled = true;
27933
+ resolve2(false);
27934
+ }
27935
+ });
27936
+ setTimeout(() => {
27937
+ if (!settled) {
27938
+ settled = true;
27939
+ logger.warn({ component: "tunnel" }, "frpc connection timeout (5s) \u2014 tunnel may not be active");
27940
+ resolve2(false);
27941
+ }
27942
+ }, 5e3);
27699
27943
  });
27700
- const url = getTunnelUrl(config3.subdomain, config3.server);
27701
- logger.info({ url, component: "tunnel" }, `Tunnel enabled \u2192 ${url}`);
27702
- return true;
27703
27944
  }
27704
27945
  function stopTunnel() {
27705
27946
  if (tunnelProcess) {
@@ -27717,13 +27958,244 @@ function cleanupConfig() {
27717
27958
  tmpConfigPath = null;
27718
27959
  }
27719
27960
  }
27720
- var tunnelProcess, tmpConfigPath;
27961
+ var tunnelProcess, tmpConfigPath, outputBuffer;
27721
27962
  var init_tunnel = __esm({
27722
27963
  "src/core/tunnel.ts"() {
27723
27964
  "use strict";
27724
27965
  init_core();
27725
27966
  tunnelProcess = null;
27726
27967
  tmpConfigPath = null;
27968
+ outputBuffer = "";
27969
+ }
27970
+ });
27971
+
27972
+ // src/cli/shared.ts
27973
+ import { consola } from "consola";
27974
+ function initCli() {
27975
+ const config3 = getLoggerConfig();
27976
+ initLoggerService({ ...config3, level: "error", sentry: void 0 });
27977
+ setLoggerInstance(getPinoLogger());
27978
+ }
27979
+ var init_shared = __esm({
27980
+ "src/cli/shared.ts"() {
27981
+ "use strict";
27982
+ init_logger();
27983
+ }
27984
+ });
27985
+
27986
+ // src/cli/migrate-commands.ts
27987
+ import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
27988
+ import { join as join14 } from "path";
27989
+ import { pathToFileURL as pathToFileURL3 } from "url";
27990
+ import Table from "cli-table3";
27991
+ import { consola as consola2 } from "consola";
27992
+ async function loadSelfPlugin() {
27993
+ const projectPath2 = getProjectPath();
27994
+ const pkgPath = join14(projectPath2, "package.json");
27995
+ if (!existsSync12(pkgPath)) return;
27996
+ try {
27997
+ const pkg2 = JSON.parse(readFileSync8(pkgPath, "utf-8"));
27998
+ const pkgName = pkg2?.name;
27999
+ if (!pkgName || !/nexus-plugin-/.test(pkgName)) return;
28000
+ const srcEntry = join14(projectPath2, "src", "index.ts");
28001
+ const distEntry = join14(projectPath2, "dist", "index.js");
28002
+ if (existsSync12(srcEntry)) {
28003
+ try {
28004
+ const { tsImport } = await import("tsx/esm/api");
28005
+ const mod = await tsImport(
28006
+ pathToFileURL3(srcEntry).href,
28007
+ import.meta.url
28008
+ );
28009
+ const manifest = extractPluginManifest(mod);
28010
+ if (manifest) {
28011
+ if (!manifest.migrationsDir) {
28012
+ manifest.migrationsDir = join14(projectPath2, "migrations");
28013
+ }
28014
+ registerPlugin(manifest);
28015
+ return;
28016
+ }
28017
+ } catch (err) {
28018
+ console.error(` \u26A0 Failed to load plugin src/index.ts: ${err.message}`);
28019
+ }
28020
+ }
28021
+ if (existsSync12(distEntry)) {
28022
+ try {
28023
+ const mod = await import(pathToFileURL3(distEntry).href);
28024
+ const manifest = extractPluginManifest(mod);
28025
+ if (manifest) {
28026
+ if (!manifest.migrationsDir) {
28027
+ manifest.migrationsDir = join14(projectPath2, "migrations");
28028
+ }
28029
+ registerPlugin(manifest);
28030
+ return;
28031
+ }
28032
+ } catch (err) {
28033
+ console.error(` \u26A0 Failed to load plugin dist/index.js: ${err.message}`);
28034
+ }
28035
+ }
28036
+ } catch {
28037
+ }
28038
+ }
28039
+ async function loadModulesForMigration() {
28040
+ loadCoreModules();
28041
+ await loadSelfPlugin();
28042
+ const config3 = await loadNexusConfig();
28043
+ if (config3.plugins?.length) {
28044
+ const sorted = topologicalSortPlugins(config3.plugins);
28045
+ for (const plugin of sorted) {
28046
+ registerPlugin(plugin);
28047
+ }
28048
+ }
28049
+ const coreNames = new Set(getOrderedModules().map((m2) => m2.name));
28050
+ for (const mod of config3.modules ?? []) {
28051
+ if (!coreNames.has(mod.name)) {
28052
+ registerModule(mod, { source: "standalone" });
28053
+ }
28054
+ }
28055
+ }
28056
+ var init_migrate_commands = __esm({
28057
+ "src/cli/migrate-commands.ts"() {
28058
+ "use strict";
28059
+ init_database();
28060
+ init_engine();
28061
+ init_load_config();
28062
+ init_shared();
28063
+ init_paths();
28064
+ init_connection();
28065
+ init_ensure_system_tables();
28066
+ init_migration_generator();
28067
+ init_migration_runner();
28068
+ }
28069
+ });
28070
+
28071
+ // src/cli/seed-commands.ts
28072
+ var seed_commands_exports = {};
28073
+ __export(seed_commands_exports, {
28074
+ handleSeedExport: () => handleSeedExport,
28075
+ importSeedFiles: () => importSeedFiles
28076
+ });
28077
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
28078
+ import { join as join15, basename as basename4 } from "path";
28079
+ import { consola as consola3 } from "consola";
28080
+ function deserializeJsonFields(record, fields) {
28081
+ const result = { ...record };
28082
+ for (const [name, field] of Object.entries(fields)) {
28083
+ if (field.db?.type === "json" && typeof result[name] === "string") {
28084
+ try {
28085
+ result[name] = JSON.parse(result[name]);
28086
+ } catch {
28087
+ }
28088
+ }
28089
+ }
28090
+ return result;
28091
+ }
28092
+ function serializeJsonFields(record, fields) {
28093
+ const result = { ...record };
28094
+ for (const [name, field] of Object.entries(fields)) {
28095
+ if (field.db?.type === "json" && typeof result[name] === "object" && result[name] !== null) {
28096
+ result[name] = JSON.stringify(result[name]);
28097
+ }
28098
+ }
28099
+ return result;
28100
+ }
28101
+ async function handleSeedExport(entity) {
28102
+ initCli();
28103
+ await loadModulesForMigration();
28104
+ const db2 = getDb();
28105
+ try {
28106
+ const modules = getOrderedModules();
28107
+ const seedDir = join15(getProjectPath(), "data", "seeds");
28108
+ const seedableEntities = [];
28109
+ for (const mod of modules) {
28110
+ for (const def of mod.definitions ?? []) {
28111
+ if (!("seedable" in def) || !def.seedable) continue;
28112
+ if (entity && def.table !== entity) continue;
28113
+ seedableEntities.push({
28114
+ module: mod.name,
28115
+ table: def.table,
28116
+ fields: def.fields
28117
+ });
28118
+ }
28119
+ }
28120
+ if (seedableEntities.length === 0) {
28121
+ if (entity) {
28122
+ consola3.warn(`No seedable entity found with table "${entity}"`);
28123
+ } else {
28124
+ consola3.warn("No seedable entities found");
28125
+ }
28126
+ return;
28127
+ }
28128
+ if (!existsSync13(seedDir)) {
28129
+ mkdirSync6(seedDir, { recursive: true });
28130
+ }
28131
+ for (const { module: modName, table, fields } of seedableEntities) {
28132
+ const rows = await db2(table).orderBy("id");
28133
+ if (rows.length === 0) {
28134
+ consola3.info(`${table}: no records, skipping`);
28135
+ continue;
28136
+ }
28137
+ const exported = rows.map(
28138
+ (row) => deserializeJsonFields(row, fields)
28139
+ );
28140
+ const filePath = join15(seedDir, `${table}.json`);
28141
+ writeFileSync3(filePath, JSON.stringify(exported, null, 2) + "\n", "utf-8");
28142
+ consola3.success(`${table}: exported ${rows.length} records to data/seeds/${table}.json (module: ${modName})`);
28143
+ }
28144
+ } catch (err) {
28145
+ consola3.error("Seed export failed:", err);
28146
+ } finally {
28147
+ await db2.destroy();
28148
+ process.exit(0);
28149
+ }
28150
+ }
28151
+ async function importSeedFiles(db2, modules, logger2) {
28152
+ const seedDir = join15(getProjectPath(), "data", "seeds");
28153
+ if (!existsSync13(seedDir)) return;
28154
+ const files = readdirSync2(seedDir).filter((f) => f.endsWith(".json"));
28155
+ if (files.length === 0) return;
28156
+ const seedableDefs = /* @__PURE__ */ new Map();
28157
+ for (const mod of modules) {
28158
+ for (const rawDef of mod.definitions ?? []) {
28159
+ const def = rawDef;
28160
+ if (!def["seedable"] || !def["table"] || !def["fields"]) continue;
28161
+ seedableDefs.set(
28162
+ def["table"],
28163
+ { fields: def["fields"], module: mod.name }
28164
+ );
28165
+ }
28166
+ }
28167
+ for (const file of files) {
28168
+ const table = basename4(file, ".json");
28169
+ const defInfo = seedableDefs.get(table);
28170
+ if (!defInfo) {
28171
+ logger2.debug(`data/seeds/${file}: skipped (entity "${table}" is not seedable)`);
28172
+ continue;
28173
+ }
28174
+ const filePath = join15(seedDir, file);
28175
+ const raw = readFileSync9(filePath, "utf-8");
28176
+ let records;
28177
+ try {
28178
+ records = JSON.parse(raw);
28179
+ } catch {
28180
+ logger2.info(`data/seeds/${file}: skipped (invalid JSON)`);
28181
+ continue;
28182
+ }
28183
+ if (!Array.isArray(records) || records.length === 0) continue;
28184
+ const rows = records.map((r) => serializeJsonFields(r, defInfo.fields));
28185
+ for (const row of rows) {
28186
+ await db2(table).insert(row).onConflict("id").merge();
28187
+ }
28188
+ logger2.info(`Seeded ${rows.length} records into ${table} from data/seeds/${file}`);
28189
+ }
28190
+ }
28191
+ var init_seed_commands = __esm({
28192
+ "src/cli/seed-commands.ts"() {
28193
+ "use strict";
28194
+ init_engine();
28195
+ init_paths();
28196
+ init_connection();
28197
+ init_shared();
28198
+ init_migrate_commands();
27727
28199
  }
27728
28200
  });
27729
28201
 
@@ -28046,6 +28518,15 @@ ${dirs}`);
28046
28518
  }
28047
28519
  const allDefinitions = modules.flatMap((m2) => m2.definitions ?? []);
28048
28520
  await createMemoryTables(allDefinitions);
28521
+ try {
28522
+ const { importSeedFiles: importSeedFiles2 } = await Promise.resolve().then(() => (init_seed_commands(), seed_commands_exports));
28523
+ await importSeedFiles2(ctx.db.knex, modules, {
28524
+ info: (msg) => logger.info(msg),
28525
+ debug: (msg) => logger.debug(msg)
28526
+ });
28527
+ } catch (err) {
28528
+ logger.debug({ err }, "Seed file import skipped (no data/seeds/ or error)");
28529
+ }
28049
28530
  logger.debug("Running seeds...");
28050
28531
  for (const mod of modules) {
28051
28532
  try {
@@ -28087,8 +28568,8 @@ async function start(config3) {
28087
28568
  }
28088
28569
  currentConfig = config3;
28089
28570
  setLocales(config3?.locales ?? DEFAULT_LOCALES2);
28090
- if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN && !env.BACKEND_URL) {
28091
- process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
28571
+ if (env.NODE_ENV === "development" && env.FRPC_SERVER && !env.TRUST_PROXY) {
28572
+ process.env["TRUST_PROXY"] = "true";
28092
28573
  }
28093
28574
  const resolved = resolveConfig();
28094
28575
  if (resolved.port > 0) {
@@ -28147,14 +28628,18 @@ async function start(config3) {
28147
28628
  eventBridge.init();
28148
28629
  const addr = server.address();
28149
28630
  const actualPort = typeof addr === "object" && addr ? addr.port : resolved.port;
28631
+ let tunnelActive = false;
28150
28632
  if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN) {
28151
- startTunnel({
28633
+ tunnelActive = await startTunnel({
28152
28634
  server: env.FRPC_SERVER,
28153
28635
  serverPort: env.FRPC_SERVER_PORT,
28154
28636
  token: env.FRPC_TOKEN,
28155
28637
  subdomain: env.FRPC_SUBDOMAIN,
28156
28638
  localPort: actualPort
28157
28639
  });
28640
+ if (tunnelActive && !env.BACKEND_URL) {
28641
+ process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
28642
+ }
28158
28643
  }
28159
28644
  const baseUrl = env.BACKEND_URL || `http://localhost:${actualPort}`;
28160
28645
  logger.debug({ libPath: getLibPath(), projectPath: getProjectPath() }, "Paths");