@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/main.js CHANGED
@@ -106,7 +106,7 @@ var init_package = __esm({
106
106
  "package.json"() {
107
107
  package_default = {
108
108
  name: "@gzl10/nexus-backend",
109
- version: "0.18.0",
109
+ version: "0.19.0",
110
110
  description: "Backend as a Service (BaaS) with Express 5, Knex and CASL",
111
111
  type: "module",
112
112
  main: "./dist/index.js",
@@ -118,6 +118,14 @@ var init_package = __esm({
118
118
  ".": {
119
119
  import: "./dist/index.js",
120
120
  types: "./dist/index.d.ts"
121
+ },
122
+ "./testing": {
123
+ import: "./dist/testing/index.js",
124
+ types: "./dist/testing/index.d.ts"
125
+ },
126
+ "./migrations": {
127
+ import: "./dist/migration-helpers/index.js",
128
+ types: "./dist/migration-helpers/index.d.ts"
121
129
  }
122
130
  },
123
131
  files: [
@@ -223,9 +231,11 @@ var init_package = __esm({
223
231
  "@types/cookie-parser": "^1.4.10",
224
232
  "@types/cors": "^2.8.19",
225
233
  "@types/express": "^5.0.6",
234
+ "@types/express-serve-static-core": "^5.1.1",
226
235
  "@types/jsonwebtoken": "^9.0.10",
227
236
  "@types/multer": "^2.1.0",
228
237
  "@types/nodemailer": "^7.0.11",
238
+ "@types/qs": "^6.15.0",
229
239
  "@types/supertest": "^6.0.3",
230
240
  "pino-pretty": "^13.1.3",
231
241
  "socket.io-client": "^4.8.3",
@@ -234,11 +244,15 @@ var init_package = __esm({
234
244
  vite: "^8.0.3"
235
245
  },
236
246
  peerDependencies: {
237
- vite: ">=6.0.0"
247
+ vite: ">=6.0.0",
248
+ vitest: ">=3.0.0"
238
249
  },
239
250
  peerDependenciesMeta: {
240
251
  vite: {
241
252
  optional: true
253
+ },
254
+ vitest: {
255
+ optional: true
242
256
  }
243
257
  },
244
258
  publishConfig: {
@@ -1140,6 +1154,14 @@ async function isWorkspacePackage(name) {
1140
1154
  }
1141
1155
  }
1142
1156
  async function installPlugin(name, opts) {
1157
+ const projectPath2 = opts?.projectPath ?? process.cwd();
1158
+ const pkgPath = join2(projectPath2, "package.json");
1159
+ if (existsSync2(pkgPath)) {
1160
+ const pkg3 = JSON.parse(readFileSync(pkgPath, "utf-8"));
1161
+ if (pkg3.name === "@gzl10/nexus-backend") {
1162
+ throw new Error("Cannot install plugins inside @gzl10/nexus-backend. Run this command from a Nexus project directory.");
1163
+ }
1164
+ }
1143
1165
  const isWorkspace = await isWorkspacePackage(name);
1144
1166
  let pkg2;
1145
1167
  if (opts?.version) {
@@ -1156,11 +1178,28 @@ async function installPlugin(name, opts) {
1156
1178
  plugins[name] = { enabled: true };
1157
1179
  writePluginsFile(plugins, filePath);
1158
1180
  }
1181
+ function isPackageInstalled(name, projectPath2) {
1182
+ const base = projectPath2 || process.cwd();
1183
+ const pkgPath = join2(base, "package.json");
1184
+ try {
1185
+ const pkg2 = JSON.parse(readFileSync(pkgPath, "utf-8"));
1186
+ return !!(pkg2.dependencies?.[name] || pkg2.devDependencies?.[name]);
1187
+ } catch {
1188
+ return false;
1189
+ }
1190
+ }
1159
1191
  async function uninstallPlugin(name, projectPath2) {
1160
- await execAsync(`pnpm remove ${name}`);
1161
1192
  const filePath = getPluginsFilePath(projectPath2);
1162
1193
  const plugins = readPluginsFile(filePath);
1163
- if (name in plugins) {
1194
+ const isRegistered = name in plugins;
1195
+ const isInstalled = isPackageInstalled(name, projectPath2);
1196
+ if (!isRegistered && !isInstalled) {
1197
+ throw new Error(`Plugin ${shortPluginName(name)} is not installed`);
1198
+ }
1199
+ if (isInstalled) {
1200
+ await execAsync(`pnpm remove ${name}`);
1201
+ }
1202
+ if (isRegistered) {
1164
1203
  delete plugins[name];
1165
1204
  writePluginsFile(plugins, filePath);
1166
1205
  }
@@ -2148,6 +2187,7 @@ var init_definitions = __esm({
2148
2187
  };
2149
2188
  mastersEntity = {
2150
2189
  table: "masters",
2190
+ seedable: true,
2151
2191
  routePrefix: "/",
2152
2192
  label: { en: "Master", es: "Maestro" },
2153
2193
  labelPlural: { en: "Masters", es: "Maestros" },
@@ -2179,14 +2219,15 @@ var init_definitions = __esm({
2179
2219
  meta: { sortable: true, searchable: true }
2180
2220
  }),
2181
2221
  label: useLocalizedField({ label: { en: "Name", es: "Nombre" } }),
2182
- order: orderField,
2222
+ order: { ...orderField, meta: { ...orderField.meta, showInDisplay: false } },
2183
2223
  is_active: isActiveField,
2184
2224
  metadata: useJsonField({
2185
2225
  label: { en: "Metadata", es: "Metadatos" },
2186
2226
  hint: {
2187
2227
  en: "Type-specific fields (symbol, flag, etc.)",
2188
2228
  es: "Campos espec\xEDficos del tipo"
2189
- }
2229
+ },
2230
+ meta: { showInDisplay: false }
2190
2231
  })
2191
2232
  },
2192
2233
  hooks: () => ({
@@ -2195,6 +2236,12 @@ var init_definitions = __esm({
2195
2236
  data["id"] = `${data["type"]}:${data["code"]}`;
2196
2237
  }
2197
2238
  return data;
2239
+ },
2240
+ beforeUpdate: async (_id, data) => {
2241
+ if ("type" in data || "code" in data) {
2242
+ throw new Error("Cannot update type or code of a master record. Delete and recreate instead.");
2243
+ }
2244
+ return data;
2198
2245
  }
2199
2246
  }),
2200
2247
  defaultSort: { field: "order", order: "asc" },
@@ -2246,7 +2293,7 @@ function createMasterRegistry() {
2246
2293
  await ctx.db.knex("masters").insert(row).onConflict(["type", "code"]).merge();
2247
2294
  }
2248
2295
  } else {
2249
- await ctx.db.knex("masters").insert(rows);
2296
+ await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
2250
2297
  }
2251
2298
  }
2252
2299
  },
@@ -8679,15 +8726,12 @@ var init_document_types = __esm({
8679
8726
  }
8680
8727
  });
8681
8728
 
8682
- // src/modules/masters/index.ts
8683
- var PREDEFINED_MASTERS, mastersModule;
8684
- var init_masters = __esm({
8685
- "src/modules/masters/index.ts"() {
8729
+ // src/modules/masters/constants.ts
8730
+ var DEFAULT_MASTER_TYPES, PREDEFINED_MASTERS;
8731
+ var init_constants = __esm({
8732
+ "src/modules/masters/constants.ts"() {
8686
8733
  "use strict";
8687
- init_definitions();
8688
- init_registry2();
8689
- init_definitions();
8690
- init_registry2();
8734
+ DEFAULT_MASTER_TYPES = ["languages", "timezones"];
8691
8735
  PREDEFINED_MASTERS = {
8692
8736
  currencies: () => Promise.resolve().then(() => (init_currencies(), currencies_exports)).then((m) => m.default),
8693
8737
  languages: () => Promise.resolve().then(() => (init_languages(), languages_exports)).then((m) => m.default),
@@ -8704,15 +8748,137 @@ var init_masters = __esm({
8704
8748
  "phone-prefixes": () => Promise.resolve().then(() => (init_phone_prefixes(), phone_prefixes_exports)).then((m) => m.default),
8705
8749
  "document-types": () => Promise.resolve().then(() => (init_document_types(), document_types_exports)).then((m) => m.default)
8706
8750
  };
8751
+ }
8752
+ });
8753
+
8754
+ // src/modules/masters/actions/install-type.ts
8755
+ import { useSelectField as useSelectField2 } from "@gzl10/nexus-sdk/fields";
8756
+ var installTypeAction;
8757
+ var init_install_type = __esm({
8758
+ "src/modules/masters/actions/install-type.ts"() {
8759
+ "use strict";
8760
+ init_constants();
8761
+ installTypeAction = {
8762
+ key: "install-type",
8763
+ scope: "module",
8764
+ label: { en: "Install Master Type", es: "Instalar Tipo de Maestro" },
8765
+ icon: "mdi:database-plus",
8766
+ variant: "primary",
8767
+ output: {},
8768
+ input: {
8769
+ type: useSelectField2({
8770
+ label: { en: "Master Type", es: "Tipo de Maestro" },
8771
+ required: true,
8772
+ options: Object.keys(PREDEFINED_MASTERS).filter((t) => !DEFAULT_MASTER_TYPES.includes(t)).map((t) => ({
8773
+ value: t,
8774
+ label: t.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
8775
+ }))
8776
+ })
8777
+ },
8778
+ handler: async (ctx, input) => {
8779
+ if (process.env["NODE_ENV"] === "production") {
8780
+ throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
8781
+ }
8782
+ const { type: type2 } = input;
8783
+ const loader = PREDEFINED_MASTERS[type2];
8784
+ if (!loader) {
8785
+ throw new ctx.core.errors.AppError(`Unknown predefined master type: ${type2}`, 400);
8786
+ }
8787
+ const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
8788
+ if (existing) {
8789
+ throw new ctx.core.errors.ConflictError(`Master type "${type2}" is already installed`);
8790
+ }
8791
+ const entries = await loader();
8792
+ const rows = entries.map((entry, i) => ({
8793
+ id: `${type2}:${entry.code}`,
8794
+ type: type2,
8795
+ code: entry.code,
8796
+ label: JSON.stringify(typeof entry.label === "string" ? { en: entry.label } : entry.label),
8797
+ order: entry.order ?? i,
8798
+ is_active: entry.is_active ?? true,
8799
+ metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
8800
+ }));
8801
+ await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
8802
+ return { installed: type2, count: rows.length };
8803
+ }
8804
+ };
8805
+ }
8806
+ });
8807
+
8808
+ // src/modules/masters/actions/uninstall-type.ts
8809
+ import { useTextField as useTextField3 } from "@gzl10/nexus-sdk/fields";
8810
+ var uninstallTypeAction;
8811
+ var init_uninstall_type = __esm({
8812
+ "src/modules/masters/actions/uninstall-type.ts"() {
8813
+ "use strict";
8814
+ init_constants();
8815
+ uninstallTypeAction = {
8816
+ key: "uninstall-type",
8817
+ scope: "module",
8818
+ label: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
8819
+ icon: "mdi:database-minus",
8820
+ variant: "danger",
8821
+ output: {},
8822
+ confirm: {
8823
+ type: "simple",
8824
+ title: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
8825
+ message: {
8826
+ en: "This will delete ALL records of the selected type. This action cannot be undone.",
8827
+ es: "Esto eliminar\xE1 TODOS los registros del tipo seleccionado. Esta acci\xF3n no se puede deshacer."
8828
+ }
8829
+ },
8830
+ input: {
8831
+ type: useTextField3({
8832
+ label: { en: "Type slug to uninstall", es: "Slug del tipo a desinstalar" },
8833
+ required: true,
8834
+ hint: {
8835
+ en: 'Enter the master type slug (e.g., "currencies", "countries")',
8836
+ es: 'Introduce el slug del tipo (ej: "currencies", "countries")'
8837
+ }
8838
+ })
8839
+ },
8840
+ handler: async (ctx, input) => {
8841
+ if (process.env["NODE_ENV"] === "production") {
8842
+ throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
8843
+ }
8844
+ const { type: type2 } = input;
8845
+ if (DEFAULT_MASTER_TYPES.includes(type2)) {
8846
+ throw new ctx.core.errors.AppError(`Cannot uninstall default master type "${type2}" (required by core)`, 400);
8847
+ }
8848
+ const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
8849
+ if (!existing) {
8850
+ throw new ctx.core.errors.NotFoundError(`Master type "${type2}" is not installed`);
8851
+ }
8852
+ const deleted = await ctx.db.knex("masters").where({ type: type2 }).del();
8853
+ return { uninstalled: type2, count: deleted };
8854
+ }
8855
+ };
8856
+ }
8857
+ });
8858
+
8859
+ // src/modules/masters/index.ts
8860
+ var mastersModule;
8861
+ var init_masters = __esm({
8862
+ "src/modules/masters/index.ts"() {
8863
+ "use strict";
8864
+ init_definitions();
8865
+ init_registry2();
8866
+ init_constants();
8867
+ init_install_type();
8868
+ init_uninstall_type();
8869
+ init_definitions();
8870
+ init_registry2();
8871
+ init_constants();
8707
8872
  mastersModule = {
8708
8873
  name: "masters",
8709
8874
  type: "core",
8710
- label: { en: "Master Data", es: "Datos maestros" },
8875
+ label: { en: "Masters", es: "Maestros" },
8711
8876
  icon: "mdi:database-outline",
8712
8877
  description: { en: "Reference data catalogs", es: "Cat\xE1logos de datos de referencia" },
8713
- category: "data",
8878
+ category: "settings",
8714
8879
  routePrefix: "/masters",
8715
8880
  definitions: [mastersEntity],
8881
+ actions: [installTypeAction, uninstallTypeAction],
8716
8882
  init: (ctx) => {
8717
8883
  if (!ctx.services.has("masters")) {
8718
8884
  ctx.services.register("masters", createMasterRegistry());
@@ -8723,13 +8889,14 @@ var init_masters = __esm({
8723
8889
  ctx.services.register("masters", createMasterRegistry());
8724
8890
  }
8725
8891
  const registry2 = ctx.services.get("masters");
8726
- const configMasters = ctx.config.resolved["masters"];
8727
- const typesToSeed = !configMasters || configMasters === "*" ? Object.keys(PREDEFINED_MASTERS) : configMasters.filter((t) => t in PREDEFINED_MASTERS);
8728
- for (const type2 of typesToSeed) {
8729
- const loader = PREDEFINED_MASTERS[type2];
8730
- if (!loader) continue;
8731
- const entries = await loader();
8732
- registry2.register(type2, entries, { seed: "if-empty" });
8892
+ const existing = await ctx.db.knex("masters").first();
8893
+ if (!existing) {
8894
+ for (const type2 of DEFAULT_MASTER_TYPES) {
8895
+ const loader = PREDEFINED_MASTERS[type2];
8896
+ if (!loader) continue;
8897
+ const entries = await loader();
8898
+ registry2.register(type2, entries, { seed: "if-empty" });
8899
+ }
8733
8900
  }
8734
8901
  await registry2.seed(ctx);
8735
8902
  }
@@ -8993,6 +9160,7 @@ function toPageDTO(page) {
8993
9160
  dataSource: page.dataSource,
8994
9161
  widgets: page.widgets,
8995
9162
  component: page.component,
9163
+ contentEndpoint: page.contentEndpoint,
8996
9164
  meta: page.meta,
8997
9165
  layout: page.layout
8998
9166
  };
@@ -9256,7 +9424,7 @@ var init_system_routes = __esm({
9256
9424
 
9257
9425
  // src/modules/system/system.entity.ts
9258
9426
  import * as os from "os";
9259
- import { useIconField, useTextField as useTextField3, useSelectField as useSelectField2, useNumberField as useNumberField2, useCheckboxField, useTagsField, useNameField, useDescriptionField } from "@gzl10/nexus-sdk/fields";
9427
+ import { useIconField, useTextField as useTextField4, useSelectField as useSelectField3, useNumberField as useNumberField2, useCheckboxField, useTagsField, useNameField, useDescriptionField } from "@gzl10/nexus-sdk/fields";
9260
9428
  var moduleEntity, osEntity;
9261
9429
  var init_system_entity = __esm({
9262
9430
  "src/modules/system/system.entity.ts"() {
@@ -9274,7 +9442,7 @@ var init_system_entity = __esm({
9274
9442
  name: useNameField({
9275
9443
  size: 50
9276
9444
  }),
9277
- label: useTextField3({
9445
+ label: useTextField4({
9278
9446
  label: { en: "Label", es: "Etiqueta" },
9279
9447
  size: 100,
9280
9448
  nullable: false,
@@ -9282,7 +9450,7 @@ var init_system_entity = __esm({
9282
9450
  }),
9283
9451
  icon: useIconField({ label: { en: "Icon", es: "Icono" }, size: 50 }),
9284
9452
  type: {
9285
- ...useSelectField2({
9453
+ ...useSelectField3({
9286
9454
  label: { en: "Type", es: "Tipo" },
9287
9455
  options: [
9288
9456
  { value: "core", label: { en: "Core", es: "Core" } },
@@ -9298,7 +9466,7 @@ var init_system_entity = __esm({
9298
9466
  description: useDescriptionField({
9299
9467
  mode: "text"
9300
9468
  }),
9301
- routePrefix: useTextField3({
9469
+ routePrefix: useTextField4({
9302
9470
  label: { en: "Route", es: "Ruta" },
9303
9471
  size: 50,
9304
9472
  nullable: true
@@ -9356,14 +9524,14 @@ var init_system_entity = __esm({
9356
9524
  order: 1,
9357
9525
  refreshInterval: 5e3,
9358
9526
  fields: {
9359
- hostname: useTextField3({
9527
+ hostname: useTextField4({
9360
9528
  label: { en: "Hostname", es: "Nombre de servidor" },
9361
9529
  size: 100,
9362
9530
  nullable: false,
9363
9531
  inputProps: { order: 1 }
9364
9532
  }),
9365
9533
  platform: {
9366
- ...useSelectField2({
9534
+ ...useSelectField3({
9367
9535
  label: { en: "Platform", es: "Plataforma" },
9368
9536
  options: [
9369
9537
  { value: "darwin", label: { en: "macOS", es: "macOS" } },
@@ -9377,7 +9545,7 @@ var init_system_entity = __esm({
9377
9545
  inputProps: { order: 2 }
9378
9546
  },
9379
9547
  arch: {
9380
- ...useSelectField2({
9548
+ ...useSelectField3({
9381
9549
  label: { en: "Architecture", es: "Arquitectura" },
9382
9550
  options: [
9383
9551
  { value: "x64", label: { en: "x64", es: "x64" } },
@@ -9390,7 +9558,7 @@ var init_system_entity = __esm({
9390
9558
  inputProps: { order: 3 }
9391
9559
  },
9392
9560
  type: {
9393
- ...useTextField3({
9561
+ ...useTextField4({
9394
9562
  label: { en: "Type", es: "Tipo" },
9395
9563
  size: 30,
9396
9564
  nullable: false
@@ -9398,7 +9566,7 @@ var init_system_entity = __esm({
9398
9566
  inputProps: { order: 4 }
9399
9567
  },
9400
9568
  release: {
9401
- ...useTextField3({
9569
+ ...useTextField4({
9402
9570
  label: { en: "Release", es: "Versi\xF3n" },
9403
9571
  size: 50,
9404
9572
  nullable: false
@@ -9421,7 +9589,7 @@ var init_system_entity = __esm({
9421
9589
  inputProps: { order: 7, format: "duration" }
9422
9590
  },
9423
9591
  cpuModel: {
9424
- ...useTextField3({
9592
+ ...useTextField4({
9425
9593
  label: { en: "CPU Model", es: "Modelo de CPU" },
9426
9594
  size: 100,
9427
9595
  nullable: false
@@ -9487,7 +9655,7 @@ var init_system_entity = __esm({
9487
9655
  });
9488
9656
 
9489
9657
  // src/modules/system/migration-history.entity.ts
9490
- import { useIdField as useIdField2, useTextField as useTextField4, useNumberField as useNumberField3, useSelectField as useSelectField3, useDatetimeField as useDatetimeField2, useTextareaField as useTextareaField2 } from "@gzl10/nexus-sdk/fields";
9658
+ import { useIdField as useIdField2, useTextField as useTextField5, useNumberField as useNumberField3, useSelectField as useSelectField4, useDatetimeField as useDatetimeField2, useTextareaField as useTextareaField2 } from "@gzl10/nexus-sdk/fields";
9491
9659
  var migrationHistoryEntity;
9492
9660
  var init_migration_history_entity = __esm({
9493
9661
  "src/modules/system/migration-history.entity.ts"() {
@@ -9504,9 +9672,9 @@ var init_migration_history_entity = __esm({
9504
9672
  calendarFrom: "executed_at",
9505
9673
  fields: {
9506
9674
  id: useIdField2(),
9507
- name: useTextField4({ label: { en: "Migration Name", es: "Nombre de la Migraci\xF3n" }, required: true, size: 255, unique: true, meta: { sortable: true, searchable: true } }),
9675
+ name: useTextField5({ label: { en: "Migration Name", es: "Nombre de la Migraci\xF3n" }, required: true, size: 255, unique: true, meta: { sortable: true, searchable: true } }),
9508
9676
  batch: useNumberField3({ label: { en: "Batch", es: "Lote" }, required: true, meta: { sortable: true } }),
9509
- status: useSelectField3({
9677
+ status: useSelectField4({
9510
9678
  label: { en: "Status", es: "Estado" },
9511
9679
  options: [
9512
9680
  { value: "running", label: { en: "Running", es: "Ejecutando" } },
@@ -9538,7 +9706,7 @@ var init_migration_history_entity = __esm({
9538
9706
  });
9539
9707
 
9540
9708
  // src/modules/system/env-config.entity.ts
9541
- import { useTextField as useTextField5, useSelectField as useSelectField4, useCheckboxField as useCheckboxField2 } from "@gzl10/nexus-sdk/fields";
9709
+ import { useTextField as useTextField6, useSelectField as useSelectField5, useCheckboxField as useCheckboxField2 } from "@gzl10/nexus-sdk/fields";
9542
9710
  function maskValue(value, sensitive) {
9543
9711
  if (!sensitive) return value;
9544
9712
  try {
@@ -9563,14 +9731,14 @@ var init_env_config_entity = __esm({
9563
9731
  routePrefix: "/env-config",
9564
9732
  defaultSort: { field: "category", order: "asc" },
9565
9733
  fields: {
9566
- name: useTextField5({
9734
+ name: useTextField6({
9567
9735
  label: { en: "Variable", es: "Variable" },
9568
9736
  size: 100,
9569
9737
  nullable: false,
9570
9738
  meta: { sortable: true, searchable: true }
9571
9739
  }),
9572
9740
  category: {
9573
- ...useSelectField4({
9741
+ ...useSelectField5({
9574
9742
  label: { en: "Category", es: "Categor\xEDa" },
9575
9743
  options: [
9576
9744
  { value: "server", label: { en: "Server", es: "Servidor" } },
@@ -9585,18 +9753,18 @@ var init_env_config_entity = __esm({
9585
9753
  meta: { sortable: true }
9586
9754
  })
9587
9755
  },
9588
- source: useTextField5({
9756
+ source: useTextField6({
9589
9757
  label: { en: "Source", es: "Origen" },
9590
9758
  size: 50,
9591
9759
  nullable: false,
9592
9760
  meta: { sortable: true }
9593
9761
  }),
9594
- value: useTextField5({
9762
+ value: useTextField6({
9595
9763
  label: { en: "Value", es: "Valor" },
9596
9764
  size: 255,
9597
9765
  nullable: true
9598
9766
  }),
9599
- default: useTextField5({
9767
+ default: useTextField6({
9600
9768
  label: { en: "Default", es: "Por defecto" },
9601
9769
  size: 100,
9602
9770
  nullable: true
@@ -10302,7 +10470,7 @@ var init_system = __esm({
10302
10470
  });
10303
10471
 
10304
10472
  // src/modules/ui-settings/ui-branding.entity.ts
10305
- import { useTextField as useTextField6, useImageField } from "@gzl10/nexus-sdk/fields";
10473
+ import { useTextField as useTextField7, useImageField } from "@gzl10/nexus-sdk/fields";
10306
10474
  var uiBrandingEntity;
10307
10475
  var init_ui_branding_entity = __esm({
10308
10476
  "src/modules/ui-settings/ui-branding.entity.ts"() {
@@ -10322,7 +10490,7 @@ var init_ui_branding_entity = __esm({
10322
10490
  favicon: null
10323
10491
  },
10324
10492
  fields: {
10325
- appName: useTextField6({
10493
+ appName: useTextField7({
10326
10494
  label: { en: "App Name", es: "Nombre de la App" },
10327
10495
  hint: { en: "Displayed in the header, browser tab and emails", es: "Se muestra en el header, pesta\xF1a del navegador y emails" },
10328
10496
  size: 100,
@@ -10363,7 +10531,7 @@ var init_ui_branding_entity = __esm({
10363
10531
  });
10364
10532
 
10365
10533
  // src/modules/ui-settings/ui-theme.entity.ts
10366
- import { useSelectField as useSelectField5, useColorField } from "@gzl10/nexus-sdk/fields";
10534
+ import { useSelectField as useSelectField6, useColorField } from "@gzl10/nexus-sdk/fields";
10367
10535
  var uiThemeEntity;
10368
10536
  var init_ui_theme_entity = __esm({
10369
10537
  "src/modules/ui-settings/ui-theme.entity.ts"() {
@@ -10388,7 +10556,7 @@ var init_ui_theme_entity = __esm({
10388
10556
  },
10389
10557
  fields: {
10390
10558
  // === Typography ===
10391
- font: useSelectField5({
10559
+ font: useSelectField6({
10392
10560
  label: { en: "Font", es: "Fuente" },
10393
10561
  hint: { en: "Primary font for headings and UI elements", es: "Fuente principal para t\xEDtulos y elementos de interfaz" },
10394
10562
  options: [
@@ -10401,7 +10569,7 @@ var init_ui_theme_entity = __esm({
10401
10569
  ]
10402
10570
  }),
10403
10571
  // === Theme & Colors ===
10404
- theme: useSelectField5({
10572
+ theme: useSelectField6({
10405
10573
  label: { en: "Theme", es: "Tema" },
10406
10574
  hint: { en: "System follows your device preferences", es: "Sistema sigue las preferencias de tu dispositivo" },
10407
10575
  options: [
@@ -10410,7 +10578,7 @@ var init_ui_theme_entity = __esm({
10410
10578
  { value: "system", label: { en: "System", es: "Sistema" } }
10411
10579
  ]
10412
10580
  }),
10413
- dopamineTheme: useSelectField5({
10581
+ dopamineTheme: useSelectField6({
10414
10582
  label: { en: "Dopamine Theme", es: "Tema Dopamina" },
10415
10583
  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)" },
10416
10584
  options: [
@@ -10428,7 +10596,7 @@ var init_ui_theme_entity = __esm({
10428
10596
  ]
10429
10597
  }),
10430
10598
  // === Login Layout ===
10431
- loginLayout: useSelectField5({
10599
+ loginLayout: useSelectField6({
10432
10600
  label: { en: "Login Layout", es: "Dise\xF1o de Login" },
10433
10601
  hint: { en: "Visual layout for authentication pages", es: "Dise\xF1o visual para p\xE1ginas de autenticaci\xF3n" },
10434
10602
  options: [
@@ -10459,7 +10627,7 @@ var init_ui_theme_entity = __esm({
10459
10627
  });
10460
10628
 
10461
10629
  // src/modules/ui-settings/ui-effects.entity.ts
10462
- import { useSelectField as useSelectField6, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
10630
+ import { useSelectField as useSelectField7, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
10463
10631
  var uiEffectsEntity;
10464
10632
  var init_ui_effects_entity = __esm({
10465
10633
  "src/modules/ui-settings/ui-effects.entity.ts"() {
@@ -10479,7 +10647,7 @@ var init_ui_effects_entity = __esm({
10479
10647
  enableOrganicShapes: false
10480
10648
  },
10481
10649
  fields: {
10482
- glassIntensity: useSelectField6({
10650
+ glassIntensity: useSelectField7({
10483
10651
  label: { en: "Glass Intensity", es: "Intensidad Glass" },
10484
10652
  hint: { en: "Glassmorphism blur effect on cards and modals", es: "Efecto de desenfoque glassmorphism en cards y modales" },
10485
10653
  options: [
@@ -10489,7 +10657,7 @@ var init_ui_effects_entity = __esm({
10489
10657
  { value: "high", label: { en: "High", es: "Alta" } }
10490
10658
  ]
10491
10659
  }),
10492
- borderStyle: useSelectField6({
10660
+ borderStyle: useSelectField7({
10493
10661
  label: { en: "Border Style", es: "Estilo de Bordes" },
10494
10662
  hint: { en: "Corner radius for buttons, cards and inputs", es: "Radio de esquinas para botones, cards e inputs" },
10495
10663
  options: [
@@ -10523,7 +10691,7 @@ var init_ui_effects_entity = __esm({
10523
10691
  });
10524
10692
 
10525
10693
  // src/modules/ui-settings/ui-accessibility.entity.ts
10526
- import { useSelectField as useSelectField7, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
10694
+ import { useSelectField as useSelectField8, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
10527
10695
  var uiAccessibilityEntity;
10528
10696
  var init_ui_accessibility_entity = __esm({
10529
10697
  "src/modules/ui-settings/ui-accessibility.entity.ts"() {
@@ -10542,7 +10710,7 @@ var init_ui_accessibility_entity = __esm({
10542
10710
  highContrast: false
10543
10711
  },
10544
10712
  fields: {
10545
- typographyScale: useSelectField7({
10713
+ typographyScale: useSelectField8({
10546
10714
  label: { en: "Typography Scale", es: "Escala Tipogr\xE1fica" },
10547
10715
  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)" },
10548
10716
  options: [
@@ -11533,7 +11701,7 @@ var init_storage_service = __esm({
11533
11701
  });
11534
11702
 
11535
11703
  // src/modules/storage/storage.entity.ts
11536
- 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";
11704
+ 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";
11537
11705
  var DEFAULT_MAX_SIZE2, storageConfigEntity, storageFilesEntity;
11538
11706
  var init_storage_entity = __esm({
11539
11707
  "src/modules/storage/storage.entity.ts"() {
@@ -11559,7 +11727,7 @@ var init_storage_entity = __esm({
11559
11727
  },
11560
11728
  fields: {
11561
11729
  id: useIdField3(),
11562
- scope: useTextField7({
11730
+ scope: useTextField8({
11563
11731
  label: { en: "Scope", es: "\xC1mbito" },
11564
11732
  disabled: true,
11565
11733
  size: 100,
@@ -11568,7 +11736,7 @@ var init_storage_entity = __esm({
11568
11736
  hint: { en: "Unique identifier (e.g. default_filesystem, default_s3)", es: "Identificador \xFAnico (ej: default_filesystem, default_s3)" }
11569
11737
  }),
11570
11738
  driver: {
11571
- ...useSelectField8({
11739
+ ...useSelectField9({
11572
11740
  label: { en: "Driver", es: "Controlador" },
11573
11741
  required: true,
11574
11742
  hint: { en: "Storage backend", es: "Backend de almacenamiento" },
@@ -11591,7 +11759,7 @@ var init_storage_entity = __esm({
11591
11759
  nullable: false,
11592
11760
  displayProps: { format: "bytes" }
11593
11761
  }),
11594
- allowed_mime_types: useTextField7({
11762
+ allowed_mime_types: useTextField8({
11595
11763
  label: { en: "Allowed MIME Types", es: "Tipos MIME permitidos" },
11596
11764
  hint: { en: "Allowed types separated by comma (e.g. image/*,application/pdf)", es: "Tipos permitidos separados por coma (ej: image/*,application/pdf)" },
11597
11765
  size: 1e3,
@@ -11756,21 +11924,21 @@ var init_storage_entity = __esm({
11756
11924
  ],
11757
11925
  fields: {
11758
11926
  id: useIdField3(),
11759
- filename: useTextField7({
11927
+ filename: useTextField8({
11760
11928
  label: { en: "Original Filename", es: "Nombre original" },
11761
11929
  required: true,
11762
11930
  size: 255,
11763
11931
  nullable: false,
11764
11932
  meta: { sortable: true, searchable: true }
11765
11933
  }),
11766
- disk_filename: useTextField7({
11934
+ disk_filename: useTextField8({
11767
11935
  label: { en: "Disk Filename", es: "Nombre en disco" },
11768
11936
  required: true,
11769
11937
  hidden: true,
11770
11938
  size: 255,
11771
11939
  nullable: false
11772
11940
  }),
11773
- mimetype: useTextField7({
11941
+ mimetype: useTextField8({
11774
11942
  label: { en: "MIME Type", es: "Tipo MIME" },
11775
11943
  required: true,
11776
11944
  size: 100,
@@ -11786,14 +11954,14 @@ var init_storage_entity = __esm({
11786
11954
  meta: { sortable: true },
11787
11955
  displayProps: { format: "bytes" }
11788
11956
  }),
11789
- folder: useTextField7({
11957
+ folder: useTextField8({
11790
11958
  label: { en: "Folder", es: "Carpeta" },
11791
11959
  size: 100,
11792
11960
  nullable: true,
11793
11961
  index: true,
11794
11962
  meta: { searchable: true }
11795
11963
  }),
11796
- scope: useTextField7({
11964
+ scope: useTextField8({
11797
11965
  label: { en: "Storage Scope", es: "\xC1mbito de almacenamiento" },
11798
11966
  hidden: true,
11799
11967
  size: 50,
@@ -11804,7 +11972,7 @@ var init_storage_entity = __esm({
11804
11972
  es: "\xC1mbito de configuraci\xF3n de almacenamiento usado para este archivo"
11805
11973
  }
11806
11974
  }),
11807
- path: useTextField7({
11975
+ path: useTextField8({
11808
11976
  label: { en: "Full Path", es: "Ruta completa" },
11809
11977
  required: true,
11810
11978
  hidden: true,
@@ -11817,13 +11985,13 @@ var init_storage_entity = __esm({
11817
11985
  nullable: true,
11818
11986
  meta: { exportable: true, showInDisplay: false }
11819
11987
  }),
11820
- thumbnail_path: useTextField7({
11988
+ thumbnail_path: useTextField8({
11821
11989
  label: { en: "Thumbnail Path", es: "Ruta de miniatura" },
11822
11990
  size: 500,
11823
11991
  nullable: true,
11824
11992
  meta: { showInDisplay: false }
11825
11993
  }),
11826
- hash: useTextField7({
11994
+ hash: useTextField8({
11827
11995
  label: { en: "SHA256 Hash", es: "Hash SHA256" },
11828
11996
  hidden: true,
11829
11997
  size: 64,
@@ -12320,7 +12488,7 @@ var init_storage = __esm({
12320
12488
  });
12321
12489
 
12322
12490
  // src/modules/users/users.entity.ts
12323
- 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";
12491
+ 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";
12324
12492
  import { z as z3 } from "zod";
12325
12493
  var userEntity, roleEntity, userRoleEntity;
12326
12494
  var init_users_entity = __esm({
@@ -12383,7 +12551,7 @@ var init_users_entity = __esm({
12383
12551
  nullable: true,
12384
12552
  meta: { exportable: true, showInForm: false, showInDisplay: false }
12385
12553
  }),
12386
- consent_version: useTextField8({
12554
+ consent_version: useTextField9({
12387
12555
  label: { en: "Consent Version", es: "Versi\xF3n de consentimiento" },
12388
12556
  hidden: true,
12389
12557
  size: 20,
@@ -12394,7 +12562,7 @@ var init_users_entity = __esm({
12394
12562
  label: { en: "Marketing Opt-in", es: "Aceptar marketing" },
12395
12563
  meta: { exportable: true, showInForm: false, showInDisplay: false }
12396
12564
  }),
12397
- locale: useSelectField9({
12565
+ locale: useSelectField10({
12398
12566
  label: { en: "Language", es: "Idioma" },
12399
12567
  options: [
12400
12568
  { value: "es", label: { en: "Spanish", es: "Espa\xF1ol" } },
@@ -12404,13 +12572,13 @@ var init_users_entity = __esm({
12404
12572
  meta: { sortable: true },
12405
12573
  defaultValue: "en"
12406
12574
  }),
12407
- timezone: useSelectField9({
12575
+ timezone: useSelectField10({
12408
12576
  label: { en: "Timezone", es: "Zona horaria" },
12409
12577
  master: "timezones",
12410
12578
  meta: { sortable: true },
12411
12579
  defaultValue: "timezones:Europe/Madrid"
12412
12580
  }),
12413
- type: useSelectField9({
12581
+ type: useSelectField10({
12414
12582
  label: { en: "Type", es: "Tipo" },
12415
12583
  defaultValue: "human",
12416
12584
  options: [
@@ -12579,7 +12747,7 @@ var init_users_entity = __esm({
12579
12747
  expose: false,
12580
12748
  fields: {
12581
12749
  id: useIdField4(),
12582
- user_id: useSelectField9({
12750
+ user_id: useSelectField10({
12583
12751
  label: { en: "User", es: "Usuario" },
12584
12752
  required: true,
12585
12753
  table: "users",
@@ -12590,7 +12758,7 @@ var init_users_entity = __esm({
12590
12758
  labelField: "name",
12591
12759
  meta: { searchable: true }
12592
12760
  }),
12593
- role_id: useSelectField9({
12761
+ role_id: useSelectField10({
12594
12762
  label: { en: "Role", es: "Rol" },
12595
12763
  required: true,
12596
12764
  table: "roles",
@@ -13534,7 +13702,7 @@ var init_users = __esm({
13534
13702
  });
13535
13703
 
13536
13704
  // src/modules/auth/auth.entity.ts
13537
- 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";
13705
+ 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";
13538
13706
  var refreshTokenEntity, authIdentitiesEntity;
13539
13707
  var init_auth_entity = __esm({
13540
13708
  "src/modules/auth/auth.entity.ts"() {
@@ -13549,7 +13717,7 @@ var init_auth_entity = __esm({
13549
13717
  expose: false,
13550
13718
  fields: {
13551
13719
  id: useIdField5(),
13552
- token: useTextField9({
13720
+ token: useTextField10({
13553
13721
  label: { en: "Token", es: "Token" },
13554
13722
  hidden: true,
13555
13723
  size: 255,
@@ -13576,14 +13744,14 @@ var init_auth_entity = __esm({
13576
13744
  nullable: true,
13577
13745
  meta: { sortable: true }
13578
13746
  }),
13579
- device_id: useTextField9({
13747
+ device_id: useTextField10({
13580
13748
  label: { en: "Device ID", es: "ID de dispositivo" },
13581
13749
  size: 64,
13582
13750
  index: true,
13583
13751
  nullable: true,
13584
13752
  meta: { searchable: true }
13585
13753
  }),
13586
- device_name: useTextField9({
13754
+ device_name: useTextField10({
13587
13755
  label: { en: "Device", es: "Dispositivo" },
13588
13756
  size: 100,
13589
13757
  nullable: true,
@@ -13615,7 +13783,7 @@ var init_auth_entity = __esm({
13615
13783
  order: 5,
13616
13784
  fields: {
13617
13785
  id: useIdField5(),
13618
- user_id: useSelectField10({
13786
+ user_id: useSelectField11({
13619
13787
  label: { en: "User", es: "Usuario" },
13620
13788
  table: "users",
13621
13789
  column: "id",
@@ -13627,7 +13795,7 @@ var init_auth_entity = __esm({
13627
13795
  labelField: "name",
13628
13796
  meta: { searchable: true }
13629
13797
  }),
13630
- provider: useTextField9({
13798
+ provider: useTextField10({
13631
13799
  label: { en: "Provider", es: "Proveedor" },
13632
13800
  required: true,
13633
13801
  size: 50,
@@ -13636,7 +13804,7 @@ var init_auth_entity = __esm({
13636
13804
  hint: { en: "e.g. pocketid, google, microsoft", es: "ej. pocketid, google, microsoft" },
13637
13805
  meta: { sortable: true, searchable: true }
13638
13806
  }),
13639
- provider_user_id: useTextField9({
13807
+ provider_user_id: useTextField10({
13640
13808
  label: { en: "Provider User ID", es: "ID de usuario del proveedor" },
13641
13809
  required: true,
13642
13810
  size: 255,
@@ -13931,7 +14099,7 @@ var init_auth_middleware = __esm({
13931
14099
  });
13932
14100
 
13933
14101
  // src/modules/auth/auth.pat.entity.ts
13934
- import { useIdField as useIdField6, useTextField as useTextField10, useSelectField as useSelectField11, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
14102
+ import { useIdField as useIdField6, useTextField as useTextField11, useSelectField as useSelectField12, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
13935
14103
  var personalTokenEntity;
13936
14104
  var init_auth_pat_entity = __esm({
13937
14105
  "src/modules/auth/auth.pat.entity.ts"() {
@@ -13948,7 +14116,7 @@ var init_auth_pat_entity = __esm({
13948
14116
  routePrefix: "/personal-tokens",
13949
14117
  fields: {
13950
14118
  id: useIdField6(),
13951
- user_id: useSelectField11({
14119
+ user_id: useSelectField12({
13952
14120
  label: { en: "User", es: "Usuario" },
13953
14121
  table: "users",
13954
14122
  column: "id",
@@ -13960,7 +14128,7 @@ var init_auth_pat_entity = __esm({
13960
14128
  labelField: "name",
13961
14129
  meta: { searchable: true }
13962
14130
  }),
13963
- name: useTextField10({
14131
+ name: useTextField11({
13964
14132
  label: { en: "Name", es: "Nombre" },
13965
14133
  required: true,
13966
14134
  size: 100,
@@ -13968,14 +14136,14 @@ var init_auth_pat_entity = __esm({
13968
14136
  hint: { en: "Descriptive name for this token", es: "Nombre descriptivo para este token" },
13969
14137
  meta: { searchable: true }
13970
14138
  }),
13971
- token_prefix: useTextField10({
14139
+ token_prefix: useTextField11({
13972
14140
  label: { en: "Token", es: "Token" },
13973
14141
  size: 20,
13974
14142
  disabled: true,
13975
14143
  nullable: false,
13976
14144
  hint: { en: "Partial token for identification", es: "Token parcial para identificaci\xF3n" }
13977
14145
  }),
13978
- token_hash: useTextField10({
14146
+ token_hash: useTextField11({
13979
14147
  label: { en: "Token Hash", es: "Hash del token" },
13980
14148
  size: 64,
13981
14149
  hidden: true,
@@ -13983,7 +14151,7 @@ var init_auth_pat_entity = __esm({
13983
14151
  unique: true,
13984
14152
  meta: { exportable: false }
13985
14153
  }),
13986
- scope: useSelectField11({
14154
+ scope: useSelectField12({
13987
14155
  label: { en: "Permission", es: "Permiso" },
13988
14156
  required: true,
13989
14157
  options: [
@@ -15739,7 +15907,7 @@ var init_mail_service = __esm({
15739
15907
  });
15740
15908
 
15741
15909
  // src/modules/mail/mail.entity.ts
15742
- 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";
15910
+ 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";
15743
15911
  import nodemailer2 from "nodemailer";
15744
15912
  async function getMailConfigFromDB(ctx) {
15745
15913
  const configService = ctx.services["config"];
@@ -15802,7 +15970,7 @@ var init_mail_entity = __esm({
15802
15970
  },
15803
15971
  fields: {
15804
15972
  id: useIdField7(),
15805
- host: useTextField11({
15973
+ host: useTextField12({
15806
15974
  label: { en: "SMTP Host", es: "Host SMTP" },
15807
15975
  size: 255,
15808
15976
  nullable: false,
@@ -15822,7 +15990,7 @@ var init_mail_entity = __esm({
15822
15990
  nullable: false,
15823
15991
  hint: { en: "Default: SMTP_FROM env var", es: "Por defecto: variable SMTP_FROM" }
15824
15992
  }),
15825
- auth_user: useTextField11({
15993
+ auth_user: useTextField12({
15826
15994
  label: { en: "Auth User", es: "Usuario de autenticaci\xF3n" },
15827
15995
  size: 255,
15828
15996
  nullable: true,
@@ -15854,12 +16022,12 @@ var init_mail_entity = __esm({
15854
16022
  required: true,
15855
16023
  validation: { format: "email" }
15856
16024
  }),
15857
- subject: useTextField11({
16025
+ subject: useTextField12({
15858
16026
  label: { en: "Subject", es: "Asunto" },
15859
16027
  required: true,
15860
16028
  validation: { min: 1, max: 255 }
15861
16029
  }),
15862
- title: useTextField11({
16030
+ title: useTextField12({
15863
16031
  label: { en: "Title", es: "T\xEDtulo" },
15864
16032
  hint: { en: "Large title in email header", es: "T\xEDtulo grande en la cabecera del correo" }
15865
16033
  }),
@@ -16003,20 +16171,20 @@ var init_mail_entity = __esm({
16003
16171
  nullable: false,
16004
16172
  meta: { sortable: true }
16005
16173
  }),
16006
- to: useTextField11({
16174
+ to: useTextField12({
16007
16175
  label: { en: "Recipient(s)", es: "Destinatario(s)" },
16008
16176
  size: 1e3,
16009
16177
  nullable: false,
16010
16178
  meta: { searchable: true }
16011
16179
  }),
16012
- subject: useTextField11({
16180
+ subject: useTextField12({
16013
16181
  label: { en: "Subject", es: "Asunto" },
16014
16182
  size: 255,
16015
16183
  nullable: false,
16016
16184
  meta: { searchable: true, sortable: true }
16017
16185
  }),
16018
16186
  status: {
16019
- ...useSelectField12({
16187
+ ...useSelectField13({
16020
16188
  label: { en: "Status", es: "Estado" },
16021
16189
  options: [
16022
16190
  { value: "pending", label: { en: "Pending", es: "Pendiente" } },
@@ -16030,7 +16198,7 @@ var init_mail_entity = __esm({
16030
16198
  }),
16031
16199
  validation: { enum: ["pending", "sent", "failed", "bounced"] }
16032
16200
  },
16033
- message_id: useTextField11({
16201
+ message_id: useTextField12({
16034
16202
  label: { en: "Message ID", es: "ID de mensaje" },
16035
16203
  hidden: true,
16036
16204
  size: 255,
@@ -16040,7 +16208,7 @@ var init_mail_entity = __esm({
16040
16208
  label: { en: "Error", es: "Error" },
16041
16209
  nullable: true
16042
16210
  }),
16043
- sent_by: useSelectField12({
16211
+ sent_by: useSelectField13({
16044
16212
  label: { en: "Sent by", es: "Enviado por" },
16045
16213
  table: "users",
16046
16214
  column: "id",
@@ -16646,7 +16814,7 @@ var init_toggle_plugin_action = __esm({
16646
16814
  });
16647
16815
 
16648
16816
  // src/modules/plugins/plugins.entity.ts
16649
- import { useTextField as useTextField12, useSelectField as useSelectField13, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16817
+ import { useTextField as useTextField13, useSelectField as useSelectField14, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
16650
16818
  import { OFFICIAL_PLUGINS } from "@gzl10/nexus-sdk";
16651
16819
  var allowPluginManagement, pluginsEntity;
16652
16820
  var init_plugins_entity = __esm({
@@ -16665,30 +16833,30 @@ var init_plugins_entity = __esm({
16665
16833
  routePrefix: "/",
16666
16834
  defaultSort: { field: "name", order: "asc" },
16667
16835
  fields: {
16668
- name: useTextField12({
16836
+ name: useTextField13({
16669
16837
  label: { en: "Name", es: "Nombre" },
16670
16838
  size: 50,
16671
16839
  nullable: false,
16672
16840
  meta: { sortable: true, searchable: true }
16673
16841
  }),
16674
- code: useTextField12({
16842
+ code: useTextField13({
16675
16843
  label: { en: "Code", es: "C\xF3digo" },
16676
16844
  size: 10,
16677
16845
  nullable: false,
16678
16846
  meta: { sortable: true }
16679
16847
  }),
16680
- label: useTextField12({
16848
+ label: useTextField13({
16681
16849
  label: { en: "Label", es: "Etiqueta" },
16682
16850
  size: 100,
16683
16851
  nullable: false,
16684
16852
  meta: { sortable: true }
16685
16853
  }),
16686
- version: useTextField12({
16854
+ version: useTextField13({
16687
16855
  label: { en: "Version", es: "Versi\xF3n" },
16688
16856
  size: 20,
16689
16857
  nullable: false
16690
16858
  }),
16691
- category: useSelectField13({
16859
+ category: useSelectField14({
16692
16860
  label: { en: "Category", es: "Categor\xEDa" },
16693
16861
  options: [
16694
16862
  { value: "content", label: { en: "Content", es: "Contenido" } },
@@ -16829,8 +16997,8 @@ var init_plugins = __esm({
16829
16997
  // src/modules/audit/audit.entity.ts
16830
16998
  import {
16831
16999
  useIdField as useIdField8,
16832
- useTextField as useTextField13,
16833
- useSelectField as useSelectField14,
17000
+ useTextField as useTextField14,
17001
+ useSelectField as useSelectField15,
16834
17002
  useTextareaField as useTextareaField4,
16835
17003
  useJsonField as useJsonField3,
16836
17004
  useDatetimeField as useDatetimeField7,
@@ -16853,7 +17021,7 @@ var init_audit_entity = __esm({
16853
17021
  fields: {
16854
17022
  id: useIdField8(),
16855
17023
  source: {
16856
- ...useTextField13({
17024
+ ...useTextField14({
16857
17025
  label: { en: "Source", es: "Origen" },
16858
17026
  size: 100,
16859
17027
  nullable: false,
@@ -16863,7 +17031,7 @@ var init_audit_entity = __esm({
16863
17031
  validation: { min: 1, max: 100 }
16864
17032
  },
16865
17033
  action: {
16866
- ...useTextField13({
17034
+ ...useTextField14({
16867
17035
  label: { en: "Action", es: "Acci\xF3n" },
16868
17036
  size: 100,
16869
17037
  nullable: false,
@@ -16872,7 +17040,7 @@ var init_audit_entity = __esm({
16872
17040
  }),
16873
17041
  validation: { min: 1, max: 100 }
16874
17042
  },
16875
- actor_id: useSelectField14({
17043
+ actor_id: useSelectField15({
16876
17044
  label: { en: "Actor", es: "Actor" },
16877
17045
  table: "users",
16878
17046
  column: "id",
@@ -16889,20 +17057,20 @@ var init_audit_entity = __esm({
16889
17057
  nullable: true,
16890
17058
  meta: { searchable: true }
16891
17059
  }),
16892
- resource_type: useTextField13({
17060
+ resource_type: useTextField14({
16893
17061
  label: { en: "Resource Type", es: "Tipo de recurso" },
16894
17062
  size: 100,
16895
17063
  nullable: true,
16896
17064
  index: true,
16897
17065
  meta: { searchable: true }
16898
17066
  }),
16899
- resource_id: useTextField13({
17067
+ resource_id: useTextField14({
16900
17068
  label: { en: "Resource ID", es: "ID del recurso" },
16901
17069
  size: 100,
16902
17070
  nullable: true,
16903
17071
  meta: { searchable: true }
16904
17072
  }),
16905
- ip_address: useTextField13({
17073
+ ip_address: useTextField14({
16906
17074
  label: { en: "IP Address", es: "Direcci\xF3n IP" },
16907
17075
  size: 45,
16908
17076
  nullable: true,
@@ -17655,6 +17823,7 @@ var init_error_codes = __esm({
17655
17823
  DB_CONSTRAINT_UNIQUE: "DB_CONSTRAINT_UNIQUE",
17656
17824
  DB_CONSTRAINT_FK: "DB_CONSTRAINT_FK",
17657
17825
  DB_CONNECTION_ERROR: "DB_CONNECTION_ERROR",
17826
+ DATABASE_NOT_READY: "DATABASE_NOT_READY",
17658
17827
  // System
17659
17828
  SYSTEM_INTERNAL_ERROR: "SYSTEM_INTERNAL_ERROR"
17660
17829
  };
@@ -17962,7 +18131,7 @@ var init_env = __esm({
17962
18131
  envSchema = z8.object({
17963
18132
  NODE_ENV: z8.enum(["development", "production", "test"]).default("development"),
17964
18133
  PORT: z8.coerce.number().default(3e3),
17965
- CORS_ORIGIN: z8.string().default("http://localhost:3001"),
18134
+ CORS_ORIGIN: z8.string().default("*"),
17966
18135
  BACKEND_URL: z8.string().optional(),
17967
18136
  DATABASE_URL: z8.string().default("file:./dev.db"),
17968
18137
  REDIS_URL: z8.string().url().optional(),
@@ -20231,10 +20400,31 @@ var init_base_service = __esm({
20231
20400
  * Override in subclasses for entity-specific search
20232
20401
  */
20233
20402
  applySearch(qb, search) {
20403
+ const searchTerm = `%${search}%`;
20404
+ const searchableFields = [];
20405
+ const fields = "fields" in this.definition ? this.definition.fields : {};
20406
+ for (const [name, field] of Object.entries(fields)) {
20407
+ if (field.meta?.searchable) {
20408
+ searchableFields.push(name);
20409
+ }
20410
+ }
20234
20411
  if ("labelField" in this.definition && this.definition.labelField) {
20235
- const labelField = this.definition.labelField;
20236
- qb.where(labelField, "like", `%${search}%`);
20412
+ const lf = this.definition.labelField;
20413
+ if (!searchableFields.includes(lf)) {
20414
+ searchableFields.unshift(lf);
20415
+ }
20237
20416
  }
20417
+ if (searchableFields.length === 0) return qb;
20418
+ qb.where(function() {
20419
+ for (const fieldName of searchableFields) {
20420
+ const field = fields[fieldName];
20421
+ if (field?.db?.type === "json") {
20422
+ this.orWhereRaw(`CAST(?? AS TEXT) LIKE ?`, [fieldName, searchTerm]);
20423
+ } else {
20424
+ this.orWhere(fieldName, "like", searchTerm);
20425
+ }
20426
+ }
20427
+ });
20238
20428
  return qb;
20239
20429
  }
20240
20430
  /**
@@ -23693,15 +23883,6 @@ var init_ensure_system_tables = __esm({
23693
23883
  }
23694
23884
  });
23695
23885
 
23696
- // src/cli/shared.ts
23697
- import { consola } from "consola";
23698
- var init_shared = __esm({
23699
- "src/cli/shared.ts"() {
23700
- "use strict";
23701
- init_logger();
23702
- }
23703
- });
23704
-
23705
23886
  // src/db/migration-sources.ts
23706
23887
  function buildMigrationSources() {
23707
23888
  const sources = [
@@ -23721,9 +23902,7 @@ function buildMigrationSources() {
23721
23902
  var init_migration_sources = __esm({
23722
23903
  "src/db/migration-sources.ts"() {
23723
23904
  "use strict";
23724
- init_shared();
23725
23905
  init_paths();
23726
- init_plugin_ops();
23727
23906
  init_module_store();
23728
23907
  }
23729
23908
  });
@@ -25001,6 +25180,10 @@ function handleDbError(err) {
25001
25180
  { path: "foreignKey", message: "Foreign key constraint violation" }
25002
25181
  ]);
25003
25182
  }
25183
+ if (code === "42P01" || code === "ER_NO_SUCH_TABLE" || dbErr.errno === 1146 || msg.includes("no such table")) {
25184
+ logger.error({ err }, "Database table not found \u2014 database may need migration or was wiped");
25185
+ throw new AppError({ code: ErrorCodes.DATABASE_NOT_READY, message: "Database not ready" }, 503);
25186
+ }
25004
25187
  logger.error({ err }, "Unexpected database error");
25005
25188
  throw new AppError({ code: ErrorCodes.SYSTEM_INTERNAL_ERROR, message: "Internal database error" }, 500);
25006
25189
  }
@@ -27283,6 +27466,33 @@ async function setupModuleRoutes(app) {
27283
27466
  }
27284
27467
  }
27285
27468
  const caslRegistry = /* @__PURE__ */ new Map();
27469
+ function mergeActionPermissions(existing, incoming) {
27470
+ const merged = { ...existing };
27471
+ for (const [role, perm] of Object.entries(incoming)) {
27472
+ if (!merged[role]) {
27473
+ merged[role] = perm;
27474
+ } else {
27475
+ const existingArr = Array.isArray(merged[role]) ? merged[role] : [merged[role]];
27476
+ const incomingArr = Array.isArray(perm) ? perm : [perm];
27477
+ merged[role] = [...existingArr, ...incomingArr];
27478
+ }
27479
+ }
27480
+ return merged;
27481
+ }
27482
+ function registerActionCasl(action) {
27483
+ const casl = action.casl;
27484
+ if (!casl || !("subject" in casl) || !casl.subject || !("permissions" in casl) || !casl.permissions) return;
27485
+ const subject2 = casl.subject;
27486
+ const existing = caslRegistry.get(subject2);
27487
+ if (existing) {
27488
+ existing.permissions = mergeActionPermissions(existing.permissions, casl.permissions);
27489
+ } else {
27490
+ caslRegistry.set(subject2, {
27491
+ subject: subject2,
27492
+ permissions: casl.permissions ?? {}
27493
+ });
27494
+ }
27495
+ }
27286
27496
  for (const mod of modules) {
27287
27497
  for (const def of mod.definitions ?? []) {
27288
27498
  const casl = def.casl;
@@ -27292,6 +27502,12 @@ async function setupModuleRoutes(app) {
27292
27502
  subject: casl.subject,
27293
27503
  permissions: casl.permissions ?? {}
27294
27504
  });
27505
+ for (const action of def.actions ?? []) {
27506
+ registerActionCasl(action);
27507
+ }
27508
+ }
27509
+ for (const action of mod.actions ?? []) {
27510
+ registerActionCasl(action);
27295
27511
  }
27296
27512
  }
27297
27513
  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((m) => m.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
 
@@ -27990,6 +28462,15 @@ ${dirs}`);
27990
28462
  }
27991
28463
  const allDefinitions = modules.flatMap((m) => m.definitions ?? []);
27992
28464
  await createMemoryTables(allDefinitions);
28465
+ try {
28466
+ const { importSeedFiles: importSeedFiles2 } = await Promise.resolve().then(() => (init_seed_commands(), seed_commands_exports));
28467
+ await importSeedFiles2(ctx.db.knex, modules, {
28468
+ info: (msg) => logger.info(msg),
28469
+ debug: (msg) => logger.debug(msg)
28470
+ });
28471
+ } catch (err) {
28472
+ logger.debug({ err }, "Seed file import skipped (no data/seeds/ or error)");
28473
+ }
27993
28474
  logger.debug("Running seeds...");
27994
28475
  for (const mod of modules) {
27995
28476
  try {
@@ -28031,8 +28512,8 @@ async function start(config3) {
28031
28512
  }
28032
28513
  currentConfig = config3;
28033
28514
  setLocales(config3?.locales ?? DEFAULT_LOCALES2);
28034
- if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN && !env.BACKEND_URL) {
28035
- process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
28515
+ if (env.NODE_ENV === "development" && env.FRPC_SERVER && !env.TRUST_PROXY) {
28516
+ process.env["TRUST_PROXY"] = "true";
28036
28517
  }
28037
28518
  const resolved = resolveConfig();
28038
28519
  if (resolved.port > 0) {
@@ -28091,14 +28572,18 @@ async function start(config3) {
28091
28572
  eventBridge.init();
28092
28573
  const addr = server.address();
28093
28574
  const actualPort = typeof addr === "object" && addr ? addr.port : resolved.port;
28575
+ let tunnelActive = false;
28094
28576
  if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN) {
28095
- startTunnel({
28577
+ tunnelActive = await startTunnel({
28096
28578
  server: env.FRPC_SERVER,
28097
28579
  serverPort: env.FRPC_SERVER_PORT,
28098
28580
  token: env.FRPC_TOKEN,
28099
28581
  subdomain: env.FRPC_SUBDOMAIN,
28100
28582
  localPort: actualPort
28101
28583
  });
28584
+ if (tunnelActive && !env.BACKEND_URL) {
28585
+ process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
28586
+ }
28102
28587
  }
28103
28588
  const baseUrl = env.BACKEND_URL || `http://localhost:${actualPort}`;
28104
28589
  logger.debug({ libPath: getLibPath(), projectPath: getProjectPath() }, "Paths");