@gzl10/nexus-backend 0.17.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.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import http from 'node:http';
1
+ import * as http from 'node:http';
2
+ import http__default from 'node:http';
2
3
  import { Express, Request, Response, Router, NextFunction, RequestHandler } from 'express';
3
4
  export { Express } from 'express';
4
5
  import { AbilityBuilder, MongoAbility, RawRuleOf } from '@casl/ability';
5
6
  import * as _gzl10_nexus_sdk from '@gzl10/nexus-sdk';
6
- import { PluginManifest, ModuleManifest, EntityQuery, PaginatedResult, EntityDefinition, ModuleContext, CreateEntityServiceOptions, BaseEntityService as BaseEntityService$1, EntityServiceHooks, CollectionEntityDefinition, SingleEntityDefinition, ViewEntityDefinition, ExternalEntityDefinition, ComputedEntityDefinition, TreeEntityDefinition, DagEntityDefinition, EntityChangePayload, BridgeRule, ValidateSchemas, SSESender, SSEHelper } from '@gzl10/nexus-sdk';
7
+ import { PluginManifest, ModuleManifest, LocaleConfig, SeedContext, EntityQuery, PaginatedResult, EntityDefinition, ModuleContext, CreateEntityServiceOptions, BaseEntityService as BaseEntityService$1, EntityServiceHooks, CollectionEntityDefinition, SingleEntityDefinition, ViewEntityDefinition, ExternalEntityDefinition, ComputedEntityDefinition, TreeEntityDefinition, DagEntityDefinition, EntityChangePayload, BridgeRule, ValidateSchemas, SSESender, SSEHelper } from '@gzl10/nexus-sdk';
7
8
  export { AuthRequest, AuthorizationParams, Category, ContextHelpers, CookieOptions, DomainFilterOptions, DomainFilterResult, EntityDefinition, EntityQuery, IdTokenClaims, ModuleAbilities, ModuleContext, ModuleManifest, ModuleMiddlewares, ModuleRequirements, NextFunction, OidcClient, OidcDiscoveryDocument, OidcState, OidcTokens, OidcUserInfo, PaginatedResult, PaginationParams, PluginManifest, Request, RequestHandler, Response, Router, SessionResult, StateManager, TokenExchangeParams, TokenValidationConfig, ValidateSchemas, assertAllowedDomain, checkAllowedDomain, createOidcClient, createStateManager, entityRoom, getOidcClient } from '@gzl10/nexus-sdk';
8
9
  import * as express_serve_static_core from 'express-serve-static-core';
9
10
  import * as knex from 'knex';
@@ -14,6 +15,8 @@ import pkg from 'eventemitter2';
14
15
  import { Server } from 'socket.io';
15
16
  import { Server as Server$1 } from 'http';
16
17
  import * as qs from 'qs';
18
+ import { A as AppError } from './app-error-CKbYJQ9V.js';
19
+ export { a as AppErrorParams, C as ConflictError, b as ErrorCode, E as ErrorCodes, F as ForbiddenError, N as NotFoundError, U as UnauthorizedError, c as ValidationDetail, V as ValidationError } from './app-error-CKbYJQ9V.js';
17
20
 
18
21
  /**
19
22
  * CASL action types (duplicated from core/abilities to avoid config/ → core/ dependency).
@@ -56,7 +59,9 @@ interface ServeSPAOptions {
56
59
  * serveSPA('/admin', '../admin/dist', { maxAge: '7d', immutable: true })
57
60
  * ```
58
61
  */
59
- type ServeSPAFunction = (endpoint: string, distPath: string, options?: ServeSPAOptions) => void;
62
+ type ServeSPAFunction = (endpoint: string, distPath: string, options?: ServeSPAOptions & {
63
+ viteSrc?: string;
64
+ }) => void | Promise<void>;
60
65
  /**
61
66
  * Configuration for a frontend SPA.
62
67
  *
@@ -89,6 +94,15 @@ interface SpaEntry extends ServeSPAOptions {
89
94
  path?: string;
90
95
  /** Origin URL for CORS allowlist. Required for external SPAs; optional for served SPAs with a custom domain. */
91
96
  origin?: string;
97
+ /**
98
+ * Path to frontend source directory (dev only).
99
+ * When set in development, mounts Vite dev server as middleware with HMR instead of serving static files.
100
+ * Ignored in production — uses `path` for static files.
101
+ * Requires `endpoint` to be set. Requires `vite` as a peer dependency.
102
+ *
103
+ * @example { endpoint: '/', viteSrc: '../ui', path: '../ui/dist' }
104
+ */
105
+ viteSrc?: string;
92
106
  }
93
107
  /**
94
108
  * Discovered plugins and modules.
@@ -110,6 +124,8 @@ interface NexusConfig {
110
124
  * Auto-discovered from src/modules/ + passed programmatically to start().
111
125
  */
112
126
  modules?: ModuleManifest[];
127
+ /** Supported locales. Default: DEFAULT_LOCALES (en + es) */
128
+ locales?: LocaleConfig[];
113
129
  }
114
130
  /**
115
131
  * Runtime options for `start()`.
@@ -147,10 +163,52 @@ interface StartOptions extends NexusConfig {
147
163
  [eventName: string]: ((data: any) => void | Promise<void>) | undefined;
148
164
  };
149
165
  /**
150
- * Function to define additional CASL rules.
151
- * Runs after loading permissions from the DB.
166
+ * Advanced CASL rules for cases not covered by `seed.roles.add({ permissions })`.
167
+ * Runs as step 3 in the ability factory (after entity definition permissions).
168
+ *
169
+ * **Prefer `onSeed` → `seed.roles.add({ permissions })` for simple role-based permissions.**
170
+ * Use `casl` only when you need:
171
+ * - Conditions: `can('update', 'Post', { authorId: user.id })`
172
+ * - Field-level: `can('read', 'User', ['name', 'email'])`
173
+ * - Denials: `cannot('delete', 'User')`
174
+ * - Dynamic logic based on user properties
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * casl: (user, { can, cannot }) => {
179
+ * if ((user as any).roleNames?.includes('EDITOR')) {
180
+ * can('update', 'Post', { authorId: (user as any).id })
181
+ * cannot('delete', 'Post')
182
+ * }
183
+ * }
184
+ * ```
152
185
  */
153
186
  casl?: CaslRulesFunction;
187
+ /**
188
+ * Callback executed after all module seeds have run.
189
+ * Receives a `SeedContext` with typed helpers — no direct DB access needed.
190
+ *
191
+ * - `seed.masters.register()` — lazy, flushed after callback completes
192
+ * - `seed.roles.add()` — eager, supports `permissions` for CASL assignment
193
+ * - `seed.users.add()` — eager, supports role assignment by name
194
+ * - `seed.plugin(name).entity(name).upsert()` — resolves table prefix automatically
195
+ * - `seed.raw()` — escape hatch for custom tables
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * onSeed: async (seed) => {
200
+ * seed.masters.register('sources', [
201
+ * { code: 'web', label: { en: 'Website', es: 'Sitio web' } }
202
+ * ])
203
+ * await seed.roles.add({
204
+ * name: 'SALES',
205
+ * permissions: { 'contacts': ['read', 'create', 'update'] }
206
+ * })
207
+ * await seed.users.add({ email: 'admin@acme.com', password: 'temp', roles: ['ADMIN'] })
208
+ * }
209
+ * ```
210
+ */
211
+ onSeed?: (seed: SeedContext) => void | Promise<void>;
154
212
  /**
155
213
  * Callback executed when the server is fully ready.
156
214
  * Runs after listen() and Socket.IO initialization.
@@ -198,9 +256,57 @@ interface ResolvedConfig {
198
256
  adminPassword?: string;
199
257
  }
200
258
 
201
- declare function start(config?: StartOptions): Promise<http.Server>;
259
+ /**
260
+ * Starts the Nexus server.
261
+ *
262
+ * Lifecycle order:
263
+ * 1. Logger, DB, migrations
264
+ * 2. Module/plugin registration and seed
265
+ * 3. `onSeed` hook (SeedContext with typed helpers)
266
+ * 4. `beforeRoutes` hook → module routes → `afterRoutes` hook
267
+ * 5. HTTP listen + Socket.IO
268
+ * 6. `onReady` hook
269
+ *
270
+ * @param config - Optional startup configuration (plugins, modules, lifecycle hooks, SPAs)
271
+ * @returns The underlying `http.Server` instance
272
+ * @throws If the server is already running (call `stop()` first)
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * import { start } from '@gzl10/nexus-backend'
277
+ *
278
+ * const server = await start({
279
+ * onSeed: async (seed) => {
280
+ * await seed.roles.add({ name: 'SALES', permissions: { 'contacts': ['read', 'create'] } })
281
+ * },
282
+ * onReady: (_app, port) => console.log(`Ready on ${port}`)
283
+ * })
284
+ * ```
285
+ */
286
+ declare function start(config?: StartOptions): Promise<http__default.Server>;
287
+ /**
288
+ * Stops the Nexus server gracefully.
289
+ *
290
+ * Closes HTTP server, Socket.IO, database connections, and cleans up all state.
291
+ * Calls `beforeClose` hook if registered. Safe to call multiple times.
292
+ *
293
+ * @example
294
+ * ```typescript
295
+ * await stop() // server stopped, port freed
296
+ * ```
297
+ */
202
298
  declare function stop(): Promise<void>;
203
- declare function restart(config?: StartOptions): Promise<http.Server>;
299
+ /**
300
+ * Restarts the Nexus server. Calls `stop()` then `start()`.
301
+ *
302
+ * If no config is provided, reuses the config from the previous `start()` call.
303
+ * Plugins, hooks, and SPAs are preserved across restarts.
304
+ *
305
+ * @param config - Optional new config. If omitted, reuses previous config.
306
+ * @returns The new `http.Server` instance
307
+ */
308
+ declare function restart(config?: StartOptions): Promise<http__default.Server>;
309
+ /** Returns `true` if the server is currently running. */
204
310
  declare function isRunning(): boolean;
205
311
 
206
312
  interface CreateAppOptions {
@@ -208,6 +314,8 @@ interface CreateAppOptions {
208
314
  afterRoutes?: (app: Express, serveSPA: ServeSPAFunction) => void | Promise<void>;
209
315
  /** Declarative SPA entries (served + CORS). Passed from StartOptions.spas. */
210
316
  spas?: SpaEntry[];
317
+ /** HTTP server for Vite HMR WebSocket (shared with Socket.IO) */
318
+ httpServer?: http.Server;
211
319
  }
212
320
  declare function createApp(options?: CreateAppOptions): Promise<express_serve_static_core.Express>;
213
321
 
@@ -2012,140 +2120,6 @@ declare function createBatchReporter(sender: SSESender): BatchReporter;
2012
2120
  */
2013
2121
  declare function createSSEHelper(): SSEHelper;
2014
2122
 
2015
- /**
2016
- * Error codes for i18n-friendly error handling.
2017
- * Frontend translates these codes to localized messages.
2018
- *
2019
- * Format: CATEGORY_ACTION_REASON
2020
- *
2021
- * Categories: AUTH, USER, ROLE, VALIDATION, STORAGE, PERMISSION, RESOURCE, SYSTEM
2022
- */
2023
- declare const ErrorCodes: {
2024
- readonly AUTH_INVALID_CREDENTIALS: "AUTH_INVALID_CREDENTIALS";
2025
- readonly AUTH_TOKEN_EXPIRED: "AUTH_TOKEN_EXPIRED";
2026
- readonly AUTH_TOKEN_INVALID: "AUTH_TOKEN_INVALID";
2027
- readonly AUTH_TOKEN_REQUIRED: "AUTH_TOKEN_REQUIRED";
2028
- readonly AUTH_OTP_REQUIRED: "AUTH_OTP_REQUIRED";
2029
- readonly AUTH_OTP_INVALID: "AUTH_OTP_INVALID";
2030
- readonly AUTH_REFRESH_TOKEN_REQUIRED: "AUTH_REFRESH_TOKEN_REQUIRED";
2031
- readonly AUTH_REFRESH_TOKEN_INVALID: "AUTH_REFRESH_TOKEN_INVALID";
2032
- readonly AUTH_REFRESH_TOKEN_EXPIRED: "AUTH_REFRESH_TOKEN_EXPIRED";
2033
- readonly AUTH_SESSION_NOT_FOUND: "AUTH_SESSION_NOT_FOUND";
2034
- readonly AUTH_SESSION_SELF_REVOKE: "AUTH_SESSION_SELF_REVOKE";
2035
- readonly AUTH_VERIFICATION_CODE_INVALID: "AUTH_VERIFICATION_CODE_INVALID";
2036
- readonly AUTH_REGISTRATION_DISABLED: "AUTH_REGISTRATION_DISABLED";
2037
- readonly AUTH_AUTO_CREATE_DISABLED: "AUTH_AUTO_CREATE_DISABLED";
2038
- readonly USER_NOT_FOUND: "USER_NOT_FOUND";
2039
- readonly USER_EMAIL_EXISTS: "USER_EMAIL_EXISTS";
2040
- readonly USER_NOT_AUTHENTICATED: "USER_NOT_AUTHENTICATED";
2041
- readonly ROLE_NOT_FOUND: "ROLE_NOT_FOUND";
2042
- readonly ROLE_NAME_EXISTS: "ROLE_NAME_EXISTS";
2043
- readonly ROLE_SYSTEM_PROTECTED: "ROLE_SYSTEM_PROTECTED";
2044
- readonly ROLE_HAS_USERS: "ROLE_HAS_USERS";
2045
- readonly ROLE_DEFAULT_NOT_FOUND: "ROLE_DEFAULT_NOT_FOUND";
2046
- readonly PERMISSION_DENIED: "PERMISSION_DENIED";
2047
- readonly VALIDATION_ERROR: "VALIDATION_ERROR";
2048
- readonly VALIDATION_FIELD_REQUIRED: "VALIDATION_FIELD_REQUIRED";
2049
- readonly VALIDATION_FIELD_INVALID: "VALIDATION_FIELD_INVALID";
2050
- readonly VALIDATION_JSON_MALFORMED: "VALIDATION_JSON_MALFORMED";
2051
- readonly STORAGE_FILE_NOT_FOUND: "STORAGE_FILE_NOT_FOUND";
2052
- readonly STORAGE_FILE_TOO_LARGE: "STORAGE_FILE_TOO_LARGE";
2053
- readonly STORAGE_FILE_TYPE_NOT_ALLOWED: "STORAGE_FILE_TYPE_NOT_ALLOWED";
2054
- readonly STORAGE_PAYLOAD_TOO_LARGE: "STORAGE_PAYLOAD_TOO_LARGE";
2055
- readonly RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND";
2056
- readonly RESOURCE_CONFLICT: "RESOURCE_CONFLICT";
2057
- readonly RESOURCE_CREATE_NOT_SUPPORTED: "RESOURCE_CREATE_NOT_SUPPORTED";
2058
- readonly RESOURCE_UPDATE_NOT_SUPPORTED: "RESOURCE_UPDATE_NOT_SUPPORTED";
2059
- readonly RESOURCE_DELETE_NOT_SUPPORTED: "RESOURCE_DELETE_NOT_SUPPORTED";
2060
- readonly MODULE_NOT_FOUND: "MODULE_NOT_FOUND";
2061
- readonly NOT_FOUND: "NOT_FOUND";
2062
- readonly AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED";
2063
- readonly DB_CONSTRAINT_UNIQUE: "DB_CONSTRAINT_UNIQUE";
2064
- readonly DB_CONSTRAINT_FK: "DB_CONSTRAINT_FK";
2065
- readonly DB_CONNECTION_ERROR: "DB_CONNECTION_ERROR";
2066
- readonly SYSTEM_INTERNAL_ERROR: "SYSTEM_INTERNAL_ERROR";
2067
- };
2068
- type ErrorCode = typeof ErrorCodes[keyof typeof ErrorCodes];
2069
-
2070
- /**
2071
- * Parameters for creating an AppError with i18n support
2072
- */
2073
- interface AppErrorParams {
2074
- /** Error code for i18n translation (e.g., 'AUTH_INVALID_CREDENTIALS') */
2075
- code: ErrorCode;
2076
- /** Fallback message in English (used if frontend doesn't have translation) */
2077
- message?: string;
2078
- /** Interpolation values for the translated message (e.g., { resource: 'User' }) */
2079
- interpolation?: Record<string, string | number>;
2080
- }
2081
- /**
2082
- * Base application error with i18n support.
2083
- *
2084
- * Can be created with:
2085
- * - String message (legacy, backward compatible)
2086
- * - AppErrorParams object (new, with code and interpolation)
2087
- *
2088
- * @example
2089
- * // Legacy usage
2090
- * throw new AppError('Something went wrong', 400)
2091
- *
2092
- * // New usage with i18n
2093
- * throw new AppError({
2094
- * code: ErrorCodes.USER_NOT_FOUND,
2095
- * message: 'User not found',
2096
- * interpolation: { resource: 'User' }
2097
- * }, 404)
2098
- */
2099
- declare class AppError extends Error {
2100
- readonly statusCode: number;
2101
- readonly code: ErrorCode;
2102
- readonly interpolation?: Record<string, string | number>;
2103
- readonly details?: unknown;
2104
- constructor(params: AppErrorParams | string, statusCode?: number, details?: unknown);
2105
- }
2106
- /**
2107
- * Resource not found error (404)
2108
- */
2109
- declare class NotFoundError$1 extends AppError {
2110
- constructor(resource?: string);
2111
- }
2112
- /**
2113
- * Authentication required error (401)
2114
- */
2115
- declare class UnauthorizedError$1 extends AppError {
2116
- constructor(codeOrMessage?: ErrorCode | string, message?: string);
2117
- }
2118
- /**
2119
- * Access denied error (403)
2120
- */
2121
- declare class ForbiddenError$1 extends AppError {
2122
- constructor(codeOrMessage?: ErrorCode | string, message?: string);
2123
- }
2124
- /**
2125
- * Resource conflict error (409)
2126
- */
2127
- declare class ConflictError$1 extends AppError {
2128
- constructor(codeOrMessage?: ErrorCode | string, message?: string);
2129
- }
2130
- /**
2131
- * Standard validation error detail
2132
- */
2133
- interface ValidationDetail {
2134
- path: string;
2135
- message: string;
2136
- /** Error code for i18n translation */
2137
- code?: string;
2138
- /** Interpolation values */
2139
- interpolation?: Record<string, string | number>;
2140
- }
2141
- /**
2142
- * Validation error with field-level details (400)
2143
- */
2144
- declare class ValidationError extends AppError {
2145
- readonly details: ValidationDetail[];
2146
- constructor(messageOrCode?: string | ErrorCode, details?: ValidationDetail[]);
2147
- }
2148
-
2149
2123
  /**
2150
2124
  * Query filter helpers.
2151
2125
  *
@@ -2576,4 +2550,4 @@ declare const core: {
2576
2550
 
2577
2551
  declare const version: string;
2578
2552
 
2579
- export { type Actions, type AppAbility, AppError, type AppErrorParams, type AppManifest, type AuditEntry, type AuditLogRow, type AuditQuery, type AuditService, BaseEntityService, type BatchReporter, type CaslRulesFunction, CollectionService, ComputedService, SingleService as ConfigService, ConflictError$1 as ConflictError, DagService, type DbEventPayload, type EntityController, type EntityHandler, type EntityRuntime, type EntityService, type ErrorCode, ErrorCodes, CollectionService as EventService, ExternalService, ForbiddenError$1 as ForbiddenError, type JwtPayload, NEXUS_CLIENT_HEADER, NXBadRequestError, NXConflictError, NXForbiddenError, NXNotFoundError, NXUnauthorizedError, type NexusConfig, type NexusEventName, type NexusEventPayload, type NexusEvents, NotFoundError$1 as NotFoundError, NxSettingsService, type RateLimitOptions, CollectionService as ReferenceService, type RefreshToken, type ResolvedConfig, type Role, type RoleWithCounts, type ServeSPAFunction, type ServeSPAOptions, SingleService, type SpaEntry, type StartOptions, type SubjectRegistry, type SubjectStrings, type Subjects, CollectionService as TempService, type TokenPair, TreeService, UnauthorizedError$1 as UnauthorizedError, type User, type UserPresence, type UserWithRoles, type UserWithoutPassword, type ValidationDetail, ValidationError, ViewService, ComputedService as VirtualService, applyFilters, asyncHandler, authAdmin, authorize, checkAbility, closeSocketIO, core, createApp, createBatchReporter, createChildLogger, createEntityRuntime, createEntityService, createLogger, createModuleRouters, createModuleServices, createNexusClientMiddleware, createRateLimit, createSSEHelper, db, defineAbilityFor, destroyDb, discoverModules, discoverPlugins, e, eventBridge, extractModuleManifests, extractPluginManifest, findEnvFile, getConfig, getConnectedUsers, getCoreManifest, getCoreModules, getDatabaseType, getDb, getIO, getLibPath, getModule, getModules, getOrderedModules, getPlugin, getPlugins, getPrismaClient, getProjectPath, getRegisteredSubjects, getServiceKey, getUserManifest, getUserModules, getUserSocketCount, hasModule, hasPlugin, hasUserApp, initSocketIO, isEntityRoom, isModuleManifest, isPluginManifest, isRunning, isSocketIOInitialized, isUserConnected, isValidSubject, loadCoreModules, loadNexusConfig, logger, m, nexusEvents, nxFeatureService, packRules, parseEntityRoom, rateLimiter, registerModule, registerPlugin, requireNexusClient, restart, serializePrisma, start, stop, unpackRules, validate, version };
2553
+ export { type Actions, type AppAbility, AppError, type AppManifest, type AuditEntry, type AuditLogRow, type AuditQuery, type AuditService, BaseEntityService, type BatchReporter, type CaslRulesFunction, CollectionService, ComputedService, SingleService as ConfigService, DagService, type DbEventPayload, type EntityController, type EntityHandler, type EntityRuntime, type EntityService, CollectionService as EventService, ExternalService, type JwtPayload, NEXUS_CLIENT_HEADER, NXBadRequestError, NXConflictError, NXForbiddenError, NXNotFoundError, NXUnauthorizedError, type NexusConfig, type NexusEventName, type NexusEventPayload, type NexusEvents, NxSettingsService, type RateLimitOptions, CollectionService as ReferenceService, type RefreshToken, type ResolvedConfig, type Role, type RoleWithCounts, type ServeSPAFunction, type ServeSPAOptions, SingleService, type SpaEntry, type StartOptions, type SubjectRegistry, type SubjectStrings, type Subjects, CollectionService as TempService, type TokenPair, TreeService, type User, type UserPresence, type UserWithRoles, type UserWithoutPassword, ViewService, ComputedService as VirtualService, applyFilters, asyncHandler, authAdmin, authorize, checkAbility, closeSocketIO, core, createApp, createBatchReporter, createChildLogger, createEntityRuntime, createEntityService, createLogger, createModuleRouters, createModuleServices, createNexusClientMiddleware, createRateLimit, createSSEHelper, db, defineAbilityFor, destroyDb, discoverModules, discoverPlugins, e, eventBridge, extractModuleManifests, extractPluginManifest, findEnvFile, getConfig, getConnectedUsers, getCoreManifest, getCoreModules, getDatabaseType, getDb, getIO, getLibPath, getModule, getModules, getOrderedModules, getPlugin, getPlugins, getPrismaClient, getProjectPath, getRegisteredSubjects, getServiceKey, getUserManifest, getUserModules, getUserSocketCount, hasModule, hasPlugin, hasUserApp, initSocketIO, isEntityRoom, isModuleManifest, isPluginManifest, isRunning, isSocketIOInitialized, isUserConnected, isValidSubject, loadCoreModules, loadNexusConfig, logger, m, nexusEvents, nxFeatureService, packRules, parseEntityRoom, rateLimiter, registerModule, registerPlugin, requireNexusClient, restart, serializePrisma, start, stop, unpackRules, validate, version };