@gzl10/nexus-backend 0.16.7 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ var __export = (target, all) => {
13
13
  import { z } from "zod";
14
14
  function resolveConfig() {
15
15
  env = envSchema.parse(process.env);
16
- process.env.TZ = env.TZ;
16
+ process.env["TZ"] = env.TZ;
17
17
  resolvedConfig = {
18
18
  nodeEnv: env.NODE_ENV,
19
19
  port: env.PORT,
@@ -68,7 +68,7 @@ var init_env = __esm({
68
68
  FRPC_SUBDOMAIN: z.string().optional()
69
69
  });
70
70
  env = envSchema.parse(process.env);
71
- process.env.TZ = env.TZ;
71
+ process.env["TZ"] = env.TZ;
72
72
  resolvedConfig = null;
73
73
  }
74
74
  });
@@ -599,12 +599,20 @@ async function discoverPlugins(projectPath2) {
599
599
  manifest.image = imagePath;
600
600
  }
601
601
  }
602
+ if (!manifest.llms) {
603
+ const llmsPath = join4(projectPath2, "node_modules", pkgName, "llms.txt");
604
+ if (existsSync3(llmsPath)) {
605
+ manifest.llms = readFileSync2(llmsPath, "utf-8");
606
+ }
607
+ }
602
608
  discovered.push(manifest);
603
609
  } else {
604
610
  logger.warn({ plugin: pkgName }, "Plugin discovery: no PluginManifest export, skipping");
605
611
  }
606
612
  } catch (err) {
607
- logger.warn({ plugin: pkgName, err: err.message }, "Plugin discovery: failed to import");
613
+ const errorMsg = err.message;
614
+ logger.warn({ plugin: pkgName, err: errorMsg }, "Plugin discovery: failed to import");
615
+ console.error(` \u26A0 Plugin '${pkgName}' failed to load: ${errorMsg}`);
608
616
  }
609
617
  }
610
618
  return discovered;
@@ -672,7 +680,9 @@ async function discoverModules(projectPath2) {
672
680
  discovered.push(...manifests);
673
681
  }
674
682
  } catch (err) {
675
- logger.warn({ module: entry, err: err.message }, "Module discovery: failed to import");
683
+ const errorMsg = err.message;
684
+ logger.warn({ module: entry, err: errorMsg }, "Module discovery: failed to import");
685
+ console.error(` \u26A0 Module '${entry}' failed to load: ${errorMsg}`);
676
686
  }
677
687
  }
678
688
  return discovered;
@@ -765,15 +775,17 @@ async function isWorkspacePackage(name) {
765
775
  }
766
776
  }
767
777
  async function installPlugin(name, opts) {
778
+ const isWorkspace = await isWorkspacePackage(name);
768
779
  let pkg3;
769
780
  if (opts?.version) {
770
781
  pkg3 = `${name}@${opts.version}`;
771
- } else if (await isWorkspacePackage(name)) {
782
+ } else if (isWorkspace) {
772
783
  pkg3 = `${name}@workspace:*`;
773
784
  } else {
774
785
  pkg3 = name;
775
786
  }
776
- await execAsync(`pnpm add -D ${pkg3}`);
787
+ const flag = isWorkspace ? "-D " : "";
788
+ await execAsync(`pnpm add ${flag}${pkg3}`);
777
789
  const filePath = getPluginsFilePath(opts?.projectPath);
778
790
  const plugins = readPluginsFile(filePath);
779
791
  plugins[name] = { enabled: true };
@@ -8422,7 +8434,7 @@ function toModuleDTO(mod, ctx) {
8422
8434
  dependencies: mod.dependencies ?? [],
8423
8435
  routePrefix: mod.routePrefix ?? `/${mod.name}`,
8424
8436
  subjects: ctx.engine.getModuleSubjects(mod),
8425
- definitions: (mod.definitions ?? []).map(
8437
+ definitions: (mod.definitions ?? []).filter((def) => ("expose" in def ? def.expose : true) !== false).map(
8426
8438
  (def) => toEntityDefinitionDTO(def, ctx.engine, mod.name)
8427
8439
  ),
8428
8440
  actions: (mod.actions ?? []).length > 0 ? (mod.actions ?? []).filter((a) => !a.hidden).map((a) => toActionDTO(a)) : void 0,
@@ -11612,7 +11624,7 @@ function createStorageRoutes(ctx) {
11612
11624
  delete filesController.create;
11613
11625
  delete filesController.update;
11614
11626
  filesController.delete = async (req, res) => {
11615
- const id = req.params["id"];
11627
+ const id = String(req.params["id"] ?? "");
11616
11628
  if (!id) throw new ValidationError2("VALIDATION_ERROR");
11617
11629
  const storageService = getStorageService();
11618
11630
  const file = await storageService.getById(id);
@@ -12048,9 +12060,9 @@ var init_users_entity = __esm({
12048
12060
  labelField: "role_id",
12049
12061
  timestamps: true,
12050
12062
  order: 15,
12051
- routePrefix: "/user-roles",
12052
12063
  hidden: true,
12053
12064
  // Pivot table - managed via Users UI
12065
+ expose: false,
12054
12066
  fields: {
12055
12067
  id: useIdField4(),
12056
12068
  user_id: useSelectField9({
@@ -12103,7 +12115,7 @@ function createUsersRoutes(ctx) {
12103
12115
  if (originalUsersDelete) {
12104
12116
  usersController.delete = async (req, res) => {
12105
12117
  const authReq = req;
12106
- const id = req.params["id"] ?? "";
12118
+ const id = String(req.params["id"] ?? "");
12107
12119
  if (authReq.user?.id === id) {
12108
12120
  throw new ctx.core.errors.ForbiddenError("No puedes eliminarte a ti mismo");
12109
12121
  }
@@ -12178,7 +12190,7 @@ function createUsersRoutes(ctx) {
12178
12190
  "/:userId/roles",
12179
12191
  auth,
12180
12192
  async (req, res) => {
12181
- const roles = await usersService.getUserRoles(req.params["userId"]);
12193
+ const roles = await usersService.getUserRoles(String(req.params["userId"] ?? ""));
12182
12194
  res.json(roles);
12183
12195
  }
12184
12196
  );
@@ -12188,18 +12200,19 @@ function createUsersRoutes(ctx) {
12188
12200
  validate2({ body: z4.object({ roleId: z4.string().min(1) }) }),
12189
12201
  async (req, res) => {
12190
12202
  const { roleId } = req.body;
12191
- await usersService.assignRole(req.params["userId"], roleId);
12203
+ const userId = String(req.params["userId"] ?? "");
12204
+ await usersService.assignRole(userId, roleId);
12192
12205
  ctx.events.notify("audit.log", {
12193
12206
  source: "core:users",
12194
12207
  action: "role_assigned",
12195
12208
  actorId: req.user?.id,
12196
12209
  resourceType: "user",
12197
- resourceId: req.params["userId"],
12210
+ resourceId: userId,
12198
12211
  ip: req.ip,
12199
12212
  userAgent: req.headers["user-agent"],
12200
12213
  metadata: { roleId }
12201
12214
  });
12202
- const roles = await usersService.getUserRoles(req.params["userId"]);
12215
+ const roles = await usersService.getUserRoles(userId);
12203
12216
  res.json(roles);
12204
12217
  }
12205
12218
  );
@@ -12209,18 +12222,19 @@ function createUsersRoutes(ctx) {
12209
12222
  validate2({ body: z4.object({ roleIds: z4.array(z4.string()) }) }),
12210
12223
  async (req, res) => {
12211
12224
  const { roleIds } = req.body;
12212
- await usersService.setRoles(req.params["userId"], roleIds);
12225
+ const userId = String(req.params["userId"] ?? "");
12226
+ await usersService.setRoles(userId, roleIds);
12213
12227
  ctx.events.notify("audit.log", {
12214
12228
  source: "core:users",
12215
12229
  action: "roles_replaced",
12216
12230
  actorId: req.user?.id,
12217
12231
  resourceType: "user",
12218
- resourceId: req.params["userId"],
12232
+ resourceId: userId,
12219
12233
  ip: req.ip,
12220
12234
  userAgent: req.headers["user-agent"],
12221
12235
  metadata: { roleIds }
12222
12236
  });
12223
- const roles = await usersService.getUserRoles(req.params["userId"]);
12237
+ const roles = await usersService.getUserRoles(userId);
12224
12238
  res.json(roles);
12225
12239
  }
12226
12240
  );
@@ -12228,16 +12242,18 @@ function createUsersRoutes(ctx) {
12228
12242
  "/:userId/roles/:roleId",
12229
12243
  auth,
12230
12244
  async (req, res) => {
12231
- await usersService.removeRole(req.params["userId"], req.params["roleId"]);
12245
+ const userId = String(req.params["userId"] ?? "");
12246
+ const roleId = String(req.params["roleId"] ?? "");
12247
+ await usersService.removeRole(userId, roleId);
12232
12248
  ctx.events.notify("audit.log", {
12233
12249
  source: "core:users",
12234
12250
  action: "role_removed",
12235
12251
  actorId: req.user?.id,
12236
12252
  resourceType: "user",
12237
- resourceId: req.params["userId"],
12253
+ resourceId: userId,
12238
12254
  ip: req.ip,
12239
12255
  userAgent: req.headers["user-agent"],
12240
- metadata: { roleId: req.params["roleId"] }
12256
+ metadata: { roleId }
12241
12257
  });
12242
12258
  res.json({ success: true });
12243
12259
  }
@@ -13015,8 +13031,8 @@ var init_auth_entity = __esm({
13015
13031
  label: { en: "Refresh Token", es: "Token de refresco" },
13016
13032
  labelPlural: { en: "Refresh Tokens", es: "Tokens de refresco" },
13017
13033
  labelField: "id",
13018
- routePrefix: "/tokens",
13019
13034
  retention: { days: 7, expiresField: "expires_at" },
13035
+ expose: false,
13020
13036
  fields: {
13021
13037
  id: useIdField5(),
13022
13038
  token: useTextField9({
@@ -13081,8 +13097,8 @@ var init_auth_entity = __esm({
13081
13097
  labelField: "provider",
13082
13098
  timestamps: true,
13083
13099
  hidden: true,
13100
+ expose: false,
13084
13101
  order: 5,
13085
- routePrefix: "/identities",
13086
13102
  fields: {
13087
13103
  id: useIdField5(),
13088
13104
  user_id: useSelectField10({
@@ -19778,7 +19794,7 @@ function createEntityController(service, definition, ctx) {
19778
19794
  * Get single entity
19779
19795
  */
19780
19796
  async get(req, res) {
19781
- const id = req.params["id"] ?? "";
19797
+ const id = String(req.params["id"] ?? "");
19782
19798
  const entity = await service.findById(id);
19783
19799
  if (!entity) {
19784
19800
  throw new ctx.core.errors.NotFoundError(`${resolveLocalized7(definition.label, "en")} not found`);
@@ -19829,7 +19845,7 @@ function createEntityController(service, definition, ctx) {
19829
19845
  if (!service.update) {
19830
19846
  throw new ctx.core.errors.ForbiddenError(`${resolveLocalized7(definition.label, "en")} does not support update`);
19831
19847
  }
19832
- const id = req.params["id"] ?? "";
19848
+ const id = String(req.params["id"] ?? "");
19833
19849
  const existing = await service.findById(id);
19834
19850
  if (!existing) {
19835
19851
  throw new ctx.core.errors.NotFoundError(`${resolveLocalized7(definition.label, "en")} not found`);
@@ -19861,7 +19877,7 @@ function createEntityController(service, definition, ctx) {
19861
19877
  if (!service.delete) {
19862
19878
  throw new ctx.core.errors.ForbiddenError(`${resolveLocalized7(definition.label, "en")} does not support delete`);
19863
19879
  }
19864
- const id = req.params["id"] ?? "";
19880
+ const id = String(req.params["id"] ?? "");
19865
19881
  const existing = await service.findById(id);
19866
19882
  if (!existing) {
19867
19883
  throw new ctx.core.errors.NotFoundError(`${resolveLocalized7(definition.label, "en")} not found`);
@@ -20060,7 +20076,7 @@ function createActionHandler(action, definition, ctx, scope = "row") {
20060
20076
  const caslSubject = (action.casl && "subject" in action.casl ? action.casl.subject : void 0) ?? definition.casl?.subject ?? capitalizeFirst(entityName ?? "Entity");
20061
20077
  const hasCasl = !action.skipAuth && (!!definition.casl || !!action.casl);
20062
20078
  return async (req, res) => {
20063
- const recordId = scope === "row" ? req.params["id"] : void 0;
20079
+ const recordId = scope === "row" ? String(req.params["id"] ?? "") : void 0;
20064
20080
  const authReq = req;
20065
20081
  if (hasCasl && !authReq.ability) {
20066
20082
  ctx.core.logger.warn({ action: "execute", subject: caslSubject, actionKey: action.key, reqId: req.requestId, decision: "deny", reason: "unauthenticated" }, "authz:deny");
@@ -20231,6 +20247,8 @@ var init_sse = __esm({
20231
20247
  // src/runtime/routes/entity.routes.ts
20232
20248
  function createEntityRouter(controller, definition, ctx, service) {
20233
20249
  const router = ctx.createRouter();
20250
+ const expose = "expose" in definition ? definition.expose : true;
20251
+ if (expose === false) return router;
20234
20252
  const type2 = definition.type ?? "collection";
20235
20253
  const isSingleton = (type2 === "single" || type2 === "config") && !("scopeField" in definition && definition.scopeField);
20236
20254
  const entityDef = definition;
@@ -20325,44 +20343,44 @@ function createEntityRouter(controller, definition, ctx, service) {
20325
20343
  res.json(await treeSvc.findRoots());
20326
20344
  }));
20327
20345
  router.post("/:id/move", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20328
- const id = req.params["id"] ?? "";
20346
+ const id = String(req.params["id"] ?? "");
20329
20347
  const { parentId } = req.body;
20330
20348
  res.json(await treeSvc.move(id, parentId ?? null));
20331
20349
  }));
20332
20350
  router.get("/:id/ancestors", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20333
- const id = req.params["id"] ?? "";
20351
+ const id = String(req.params["id"] ?? "");
20334
20352
  res.json(await treeSvc.getAncestors(id));
20335
20353
  }));
20336
20354
  router.get("/:id/descendants", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20337
- const id = req.params["id"] ?? "";
20355
+ const id = String(req.params["id"] ?? "");
20338
20356
  res.json(await treeSvc.getDescendants(id));
20339
20357
  }));
20340
20358
  router.get("/:id/children", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20341
- const id = req.params["id"] ?? "";
20359
+ const id = String(req.params["id"] ?? "");
20342
20360
  res.json(await treeSvc.findChildren(id));
20343
20361
  }));
20344
20362
  }
20345
20363
  if (type2 === "dag" && service) {
20346
20364
  const dagSvc = service;
20347
20365
  router.get("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20348
- const id = req.params["id"] ?? "";
20366
+ const id = String(req.params["id"] ?? "");
20349
20367
  res.json(await dagSvc.getParents(id));
20350
20368
  }));
20351
20369
  router.put("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20352
- const id = req.params["id"] ?? "";
20370
+ const id = String(req.params["id"] ?? "");
20353
20371
  const { parentIds } = req.body;
20354
20372
  await dagSvc.setParents(id, parentIds);
20355
20373
  res.status(204).send();
20356
20374
  }));
20357
20375
  router.post("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20358
- const id = req.params["id"] ?? "";
20376
+ const id = String(req.params["id"] ?? "");
20359
20377
  const { parentId } = req.body;
20360
20378
  await dagSvc.addParent(id, parentId);
20361
20379
  res.status(204).send();
20362
20380
  }));
20363
20381
  router.delete("/:id/parents/:parentId", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
20364
- const id = req.params["id"] ?? "";
20365
- const parentId = req.params["parentId"] ?? "";
20382
+ const id = String(req.params["id"] ?? "");
20383
+ const parentId = String(req.params["parentId"] ?? "");
20366
20384
  await dagSvc.removeParent(id, parentId);
20367
20385
  res.status(204).send();
20368
20386
  }));
@@ -22989,7 +23007,20 @@ Applying ${pendingBefore.length} pending migration(s)...
22989
23007
  const filepath = await generateMigrationForSource(migrationName, effectiveScope);
22990
23008
  if (filepath) {
22991
23009
  console.log("Applying new migration...\n");
22992
- await runMigrations2();
23010
+ try {
23011
+ await runMigrations2();
23012
+ } catch (applyError) {
23013
+ console.error("\n\u274C Migration apply failed \u2014 cleaning up generated file...\n");
23014
+ try {
23015
+ await fs2.unlink(filepath);
23016
+ console.error(`Removed: ${path2.basename(filepath)}
23017
+ `);
23018
+ } catch {
23019
+ console.error(`Could not remove ${filepath} \u2014 please delete it manually.
23020
+ `);
23021
+ }
23022
+ throw applyError;
23023
+ }
22993
23024
  console.log("\n\u2705 Migration applied successfully\n");
22994
23025
  } else if (pendingBefore.length === 0) {
22995
23026
  logger.info({ scope: effectiveScope }, "No changes detected and no pending migrations");
@@ -23223,10 +23254,17 @@ function normalizeColumnType(type2) {
23223
23254
  "JSON": "JSON",
23224
23255
  "JSONB": "JSON",
23225
23256
  "UUID": "UUID",
23226
- // PostgreSQL specific
23257
+ // PostgreSQL specific (information_schema.columns.data_type values)
23227
23258
  "CHARACTER VARYING": "STRING",
23259
+ "CHARACTER": "STRING",
23260
+ "DOUBLE PRECISION": "DECIMAL",
23261
+ "REAL": "DECIMAL",
23262
+ "SMALLINT": "INTEGER",
23228
23263
  "TIMESTAMP WITHOUT TIME ZONE": "DATETIME",
23229
23264
  "TIMESTAMP WITH TIME ZONE": "DATETIME",
23265
+ "TIME WITHOUT TIME ZONE": "TIME",
23266
+ "TIME WITH TIME ZONE": "TIME",
23267
+ "BIT VARYING": "STRING",
23230
23268
  // MySQL specific
23231
23269
  "TINYINT": "BOOLEAN",
23232
23270
  "LONGTEXT": "TEXT",
@@ -26293,7 +26331,8 @@ async function loadSelfPlugin() {
26293
26331
  registerPlugin(manifest);
26294
26332
  return;
26295
26333
  }
26296
- } catch {
26334
+ } catch (err) {
26335
+ console.error(` \u26A0 Failed to load plugin src/index.ts: ${err.message}`);
26297
26336
  }
26298
26337
  }
26299
26338
  if (existsSync11(distEntry)) {
@@ -26307,7 +26346,8 @@ async function loadSelfPlugin() {
26307
26346
  registerPlugin(manifest);
26308
26347
  return;
26309
26348
  }
26310
- } catch {
26349
+ } catch (err) {
26350
+ console.error(` \u26A0 Failed to load plugin dist/index.js: ${err.message}`);
26311
26351
  }
26312
26352
  }
26313
26353
  } catch {
@@ -26857,6 +26897,10 @@ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
26857
26897
  import { join as join16 } from "path";
26858
26898
  import Table2 from "cli-table3";
26859
26899
  import { consola as consola3 } from "consola";
26900
+ import { OFFICIAL_PLUGINS as OFFICIAL_PLUGINS2 } from "@gzl10/nexus-sdk";
26901
+ function pluginLabel(name) {
26902
+ return shortPluginName(name).split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
26903
+ }
26860
26904
  function readPluginVersion(projectPath2, pkgName) {
26861
26905
  const pkgJsonPath = join16(projectPath2, "node_modules", pkgName, "package.json");
26862
26906
  if (!existsSync12(pkgJsonPath)) return null;
@@ -26918,12 +26962,12 @@ async function handlePluginList(options = {}) {
26918
26962
  } else {
26919
26963
  consola3.info("No plugins installed.");
26920
26964
  }
26921
- const available = KNOWN_PLUGINS.filter((p) => !installedNames.has(p.name));
26965
+ const available = OFFICIAL_PLUGINS2.filter((name) => !installedNames.has(name));
26922
26966
  if (available.length > 0) {
26923
26967
  console.log("\n Available (not installed):");
26924
26968
  const availTable = new Table2({ head: ["Name", "Package"] });
26925
- for (const p of available) {
26926
- availTable.push([p.label, p.name]);
26969
+ for (const name of available) {
26970
+ availTable.push([pluginLabel(name), name]);
26927
26971
  }
26928
26972
  console.log(availTable.toString());
26929
26973
  console.log(`
@@ -27046,7 +27090,6 @@ async function handlePluginInfo(name) {
27046
27090
  }
27047
27091
  console.log("");
27048
27092
  }
27049
- var KNOWN_PLUGINS;
27050
27093
  var init_plugin_commands = __esm({
27051
27094
  "src/cli/plugin-commands.ts"() {
27052
27095
  "use strict";
@@ -27054,22 +27097,6 @@ var init_plugin_commands = __esm({
27054
27097
  init_paths();
27055
27098
  init_shared();
27056
27099
  init_plugin_ops();
27057
- KNOWN_PLUGINS = [
27058
- { name: "@gzl10/nexus-plugin-ai", label: "AI" },
27059
- { name: "@gzl10/nexus-plugin-auth-providers", label: "Auth Providers" },
27060
- { name: "@gzl10/nexus-plugin-charts", label: "Charts" },
27061
- { name: "@gzl10/nexus-plugin-compliance", label: "Compliance" },
27062
- { name: "@gzl10/nexus-plugin-docker", label: "Docker" },
27063
- { name: "@gzl10/nexus-plugin-links", label: "Links" },
27064
- { name: "@gzl10/nexus-plugin-notifications", label: "Notifications" },
27065
- { name: "@gzl10/nexus-plugin-oidc-server", label: "OIDC Server" },
27066
- { name: "@gzl10/nexus-plugin-plane", label: "Plane" },
27067
- { name: "@gzl10/nexus-plugin-prisma", label: "Prisma" },
27068
- { name: "@gzl10/nexus-plugin-remote", label: "Remote" },
27069
- { name: "@gzl10/nexus-plugin-schedules", label: "Schedules" },
27070
- { name: "@gzl10/nexus-plugin-tags", label: "Tags" },
27071
- { name: "@gzl10/nexus-plugin-webhooks", label: "Webhooks" }
27072
- ];
27073
27100
  }
27074
27101
  });
27075
27102