@gzl10/nexus-sdk 0.12.7 → 0.13.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/chunk-6WVPKBUS.js +364 -0
- package/dist/chunk-JIEVWBDA.js +256 -0
- package/dist/chunk-WZM5DHUI.js +377 -0
- package/dist/collection/index.d.ts +506 -0
- package/dist/collection/index.js +1501 -0
- package/dist/entity-kPgsCMdR.d.ts +1504 -0
- package/dist/field-CngHXora.d.ts +375 -0
- package/dist/fields/index.d.ts +1305 -0
- package/dist/fields/index.js +58 -0
- package/dist/index.d.ts +387 -2302
- package/dist/index.js +25 -69
- package/dist/masters/index.d.ts +120 -0
- package/dist/masters/index.js +689 -0
- package/package.json +23 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,2257 +1,29 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
2
|
export { Knex } from 'knex';
|
|
3
|
-
import { Request, Router, RequestHandler, Response } from 'express';
|
|
4
3
|
export { CookieOptions, NextFunction, Request, RequestHandler, Response, Router } from 'express';
|
|
5
|
-
import {
|
|
4
|
+
import { E as EntityType, D as DisplayMode, C as Category, a as EntityDefinition, b as CollectionEntityDefinition, R as ReferenceEntityDefinition, c as EventEntityDefinition, d as ConfigEntityDefinition, T as TempEntityDefinition, V as ViewEntityDefinition, e as TreeEntityDefinition, f as DagEntityDefinition, S as SingleEntityDefinition, A as ActionDefinition, g as ExternalEntityDefinition, h as VirtualEntityDefinition, i as ComputedEntityDefinition } from './entity-kPgsCMdR.js';
|
|
5
|
+
export { w as AbilityLike, $ as ActionEntityDefinition, ab as ActionEntityService, aN as ActionInjection, aG as AdaptersContext, aP as AppManifest, y as AuthRequest, o as BaseAuthRequest, a0 as BaseEntityService, ai as BaseMailService, ae as BaseRolesService, p as BaseUser, af as BaseUsersService, B as BatchConfig, n as BatchLogEntry, m as BatchProgressEvent, aW as CATEGORIES, aX as CATEGORY_ORDER, t as CaslAction, aM as CategoryMeta, a1 as CollectionEntityService, a8 as ComputedEntityService, aD as ConfigContext, a5 as ConfigEntityService, at as ContextCache, aq as ContextCrypto, ax as ContextHelpers, as as ContextLRUCache, ar as ContextLRUCacheOptions, aw as ContextSocket, aA as CoreContext, ak as CoreServices, ad as CreateEntityServiceOptions, aL as CustomRouteDefinition, I as DatabaseAdapter, aB as DbContext, aE as EngineContext, W as EntityAction, v as EntityCaslConfig, az as EntityController, ay as EntityHandler, H as EntityQuery, ac as EntityServiceHooks, ap as EventEmitterLike, a3 as EventEntityService, a9 as ExternalEntityService, z as FilterOperators, G as FilterValue, x as ForbiddenErrorConstructor, F as ForbiddenErrorInstance, aI as HttpMethod, aj as LoggerReporter, M as ModuleAbilities, aH as ModuleContext, aO as ModuleManifest, ao as ModuleMiddlewares, U as ModuleRequirements, r as PaginatedResult, P as PaginationParams, q as PaginationParamsWithOffset, aR as PluginConfigField, aS as PluginConfigSchema, aQ as PluginEnvVar, aU as PluginManifest, aT as PluginSetupInfo, an as RateLimitOptions, a6 as ReferenceEntityService, aV as RegisterPluginOptions, Z as RetentionPolicy, u as RolePermission, aJ as RouteParameterDefinition, aK as RouteResponseDefinition, aC as RuntimeContext, j as SSEHelper, l as SSEOptions, k as SSESender, Q as SchemaAdapter, O as SchemaAlterTableBuilder, K as SchemaColumnBuilder, J as SchemaColumnInfo, N as SchemaForeignKeyBuilder, L as SchemaTableBuilder, Y as SeedConfig, ag as SendMailOptions, ah as SendMailResult, aF as ServicesContext, a4 as SingleEntityService, av as SocketIOBroadcastOperator, au as SocketIOServer, X as StandaloneAction, a2 as TempEntityService, _ as TempRetentionPolicy, am as ValidateSchemas, s as ValidationDetail, al as ValidationSchema, a7 as ViewEntityService, aa as VirtualEntityService } from './entity-kPgsCMdR.js';
|
|
6
|
+
import { L as LocalizedString, I as InputType, F as FieldCondition, a as FieldMeta } from './field-CngHXora.js';
|
|
7
|
+
export { A as AuthProviderInfo, b as AuthProviderService, C as ConditionalBoolean, D as DbType, E as EntityIndex, c as FieldDbConfig, i as FieldDefinition, f as FieldOptions, d as FieldRelation, h as FieldStorageConfig, e as FieldValidationConfig, g as getCountLabel, k as refMaster, j as refOptions, r as resolveLocalized } from './field-CngHXora.js';
|
|
8
|
+
import 'pino';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Entities that persist in the local DB with their own table.
|
|
13
|
-
* Excludes: action, external, virtual, computed, single (uses shared table).
|
|
14
|
-
*/
|
|
15
|
-
type PersistentEntityDefinition = CollectionEntityDefinition | ReferenceEntityDefinition | EventEntityDefinition | ConfigEntityDefinition | TempEntityDefinition | ViewEntityDefinition;
|
|
16
|
-
/**
|
|
17
|
-
* Entities without local persistence (read-only or without DB)
|
|
18
|
-
*/
|
|
19
|
-
type NonPersistentEntityDefinition = ActionEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition;
|
|
20
|
-
/**
|
|
21
|
-
* Type guard to check whether an entity persists in the local DB with its own table
|
|
22
|
-
*/
|
|
23
|
-
declare function isPersistentEntity(entity: EntityDefinition): entity is PersistentEntityDefinition;
|
|
24
|
-
/**
|
|
25
|
-
* Type guard to check whether it is a singleton (uses shared sys_settings table)
|
|
26
|
-
*/
|
|
27
|
-
declare function isSingletonEntity(entity: EntityDefinition): entity is SingleEntityDefinition;
|
|
28
|
-
/**
|
|
29
|
-
* Type guard to check whether an entity has its own table (for migrations)
|
|
30
|
-
*/
|
|
31
|
-
declare function hasTable(entity: EntityDefinition): entity is PersistentEntityDefinition;
|
|
32
|
-
/**
|
|
33
|
-
* Gets an entity name in singular PascalCase.
|
|
34
|
-
* 'cms_posts' → 'Post', 'rol_role_permissions' → 'RolePermission'
|
|
35
|
-
*/
|
|
36
|
-
declare function getEntityName(entity: EntityDefinition): string;
|
|
37
|
-
/**
|
|
38
|
-
* Gets the CASL subject for an entity
|
|
39
|
-
*/
|
|
40
|
-
declare function getEntitySubject(entity: PersistentEntityDefinition): string;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @gzl10/nexus-sdk
|
|
44
|
-
*
|
|
45
|
-
* SDK types for creating Nexus plugins and modules.
|
|
46
|
-
* Use this package to define plugin/module manifests without
|
|
47
|
-
* depending on the full @gzl10/nexus-backend package.
|
|
48
|
-
*/
|
|
49
|
-
/**
|
|
50
|
-
* Discriminated union of all entity types
|
|
51
|
-
*
|
|
52
|
-
* | Type | Persistence | CRUD | Primary use |
|
|
53
|
-
* |------------|-------------|-----------------|----------------------------------|
|
|
54
|
-
* | collection | Yes (DB) | Full | Business data (users, posts) |
|
|
55
|
-
* | single | Yes (DB) | Update/Read | Global config (site_config) |
|
|
56
|
-
* | external | No | Read | External data (stripe_customers) |
|
|
57
|
-
* | virtual | No | Read | Orchestration (unified_customers)|
|
|
58
|
-
* | computed | No/optional | Read | KPIs, stats |
|
|
59
|
-
* | view | Yes/virtual | Read | Optimized reads (projections) |
|
|
60
|
-
* | reference | Yes | Read (admin) | Catalogs (countries, currencies) |
|
|
61
|
-
* | config | Yes | Update/Read | Per-module config |
|
|
62
|
-
* | event | Yes | Append | Audit logs (audit_logs) |
|
|
63
|
-
* | temp | No (TTL) | Read/Write | Cache, sessions (otp_codes) |
|
|
64
|
-
* | action | No | Execute | Operations, workflows |
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
type KnexCreateTableBuilder = Knex.CreateTableBuilder;
|
|
68
|
-
type KnexAlterTableBuilder = Knex.AlterTableBuilder;
|
|
69
|
-
type KnexTransaction = Knex.Transaction;
|
|
70
|
-
/**
|
|
71
|
-
* Base authenticated request with user and CASL abilities (generic).
|
|
72
|
-
* Use AuthRequest for the pre-typed version.
|
|
73
|
-
*/
|
|
74
|
-
interface BaseAuthRequest<TUser = unknown, TAbility = unknown> extends Request {
|
|
75
|
-
user: TUser;
|
|
76
|
-
ability: TAbility;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Base user without a password for plugin use.
|
|
80
|
-
* Used to type relations with users (e.g., author, created_by).
|
|
81
|
-
* Note: Multi-role support - roles are loaded via user_roles pivot table.
|
|
82
|
-
*/
|
|
83
|
-
interface BaseUser {
|
|
84
|
-
id: string;
|
|
85
|
-
name: string;
|
|
86
|
-
email: string;
|
|
87
|
-
created_at: Date;
|
|
88
|
-
updated_at: Date;
|
|
89
|
-
created_by: string | null;
|
|
90
|
-
updated_by: string | null;
|
|
91
|
-
/** Extensibility: allows custom user properties */
|
|
92
|
-
[key: string]: unknown;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Pagination parameters for queries
|
|
96
|
-
*/
|
|
97
|
-
interface PaginationParams {
|
|
98
|
-
page: number;
|
|
99
|
-
limit: number;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Generic paginated result
|
|
103
|
-
*/
|
|
104
|
-
interface PaginatedResult<T> {
|
|
105
|
-
items: T[];
|
|
106
|
-
total: number;
|
|
107
|
-
page: number;
|
|
108
|
-
limit: number;
|
|
109
|
-
totalPages: number;
|
|
110
|
-
hasNext: boolean;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Standard validation error detail for API responses
|
|
114
|
-
*/
|
|
115
|
-
interface ValidationDetail {
|
|
116
|
-
path: string;
|
|
117
|
-
message: string;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Localized string supporting multiple languages.
|
|
121
|
-
* Can be a simple string (single language) or object with language codes.
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* // Simple string (backward compatible)
|
|
125
|
-
* label: 'Users'
|
|
126
|
-
*
|
|
127
|
-
* // Multi-language object
|
|
128
|
-
* label: { en: 'Users', es: 'Usuarios' }
|
|
129
|
-
*/
|
|
130
|
-
type LocalizedString = string | Record<string, string>;
|
|
131
|
-
/**
|
|
132
|
-
* Resolves a LocalizedString to the appropriate language.
|
|
133
|
-
* Falls back to 'en', then first available, then empty string.
|
|
134
|
-
*
|
|
135
|
-
* @param value - The localized string to resolve
|
|
136
|
-
* @param locale - The target locale (e.g., 'en', 'es')
|
|
137
|
-
* @returns The resolved string for the given locale
|
|
138
|
-
*/
|
|
139
|
-
declare function resolveLocalized(value: LocalizedString | undefined, locale: string): string;
|
|
140
|
-
/**
|
|
141
|
-
* Gets the appropriate label based on count (pluralization).
|
|
142
|
-
* Returns labelPlural for count !== 1, label otherwise.
|
|
143
|
-
*
|
|
144
|
-
* @param label - Singular form
|
|
145
|
-
* @param labelPlural - Plural form (optional, defaults to label)
|
|
146
|
-
* @param count - The count to determine singular/plural
|
|
147
|
-
* @param locale - The target locale
|
|
148
|
-
* @returns The resolved string for the given count and locale
|
|
149
|
-
*/
|
|
150
|
-
declare function getCountLabel(label: LocalizedString | undefined, labelPlural: LocalizedString | undefined, count: number, locale: string): string;
|
|
151
|
-
/**
|
|
152
|
-
* Database column types for Knex migrations.
|
|
153
|
-
*
|
|
154
|
-
* Maps to SQL column types:
|
|
155
|
-
* - `string` → VARCHAR(size) - Use with `db.size` for length
|
|
156
|
-
* - `text` → TEXT - Unlimited length text
|
|
157
|
-
* - `integer` → INTEGER - Whole numbers
|
|
158
|
-
* - `decimal` → DECIMAL(p,s) - Use with `db.precision` for scale
|
|
159
|
-
* - `boolean` → BOOLEAN - True/false values
|
|
160
|
-
* - `date` → DATE - Date without time
|
|
161
|
-
* - `datetime` → DATETIME/TIMESTAMP - Date with time
|
|
162
|
-
* - `json` → JSON/JSONB - Structured data (use for LocalizedString)
|
|
163
|
-
* - `uuid` → UUID - Universally unique identifier
|
|
164
|
-
* - `array` → Virtual only - No database column generated
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* ```typescript
|
|
168
|
-
* db: { type: 'string', size: 100 } // VARCHAR(100)
|
|
169
|
-
* db: { type: 'decimal', precision: [10, 2] } // DECIMAL(10,2)
|
|
170
|
-
* db: { type: 'json', nullable: false } // JSON NOT NULL
|
|
171
|
-
* ```
|
|
172
|
-
*/
|
|
173
|
-
type DbType = 'string' | 'text' | 'integer' | 'decimal' | 'boolean' | 'date' | 'datetime' | 'json' | 'uuid' | 'array';
|
|
174
|
-
/**
|
|
175
|
-
* UI input types that determine which form component renders.
|
|
176
|
-
*
|
|
177
|
-
* **Text inputs:**
|
|
178
|
-
* - `text` → NInput - Single line text
|
|
179
|
-
* - `email` → NInput with email validation
|
|
180
|
-
* - `password` → NInput with password mask
|
|
181
|
-
* - `url` → NInput with URL validation
|
|
182
|
-
* - `tel` → NInput for phone numbers
|
|
183
|
-
*
|
|
184
|
-
* **Numeric inputs:**
|
|
185
|
-
* - `number` → NInputNumber - Integer values
|
|
186
|
-
* - `decimal` → NInputNumber with decimal precision
|
|
187
|
-
*
|
|
188
|
-
* **Rich text:**
|
|
189
|
-
* - `textarea` → NInput multiline
|
|
190
|
-
* - `markdown` → MarkdownInput with preview
|
|
191
|
-
* - `json` → JsonInput with syntax highlighting
|
|
192
|
-
*
|
|
193
|
-
* **Selection:**
|
|
194
|
-
* - `select` → NSelect - Single selection dropdown
|
|
195
|
-
* - `multiselect` → NSelect with multiple - Multiple selection
|
|
196
|
-
* - `transfer` → NTransfer - Dual list selection
|
|
197
|
-
* - `tags` → NSelect with tags - Free-form tags input
|
|
198
|
-
*
|
|
199
|
-
* **Boolean:**
|
|
200
|
-
* - `checkbox` → NCheckbox
|
|
201
|
-
* - `switch` → NSwitch - Toggle switch
|
|
202
|
-
*
|
|
203
|
-
* **Date/Time:**
|
|
204
|
-
* - `date` → NDatePicker - Date only
|
|
205
|
-
* - `datetime` → NDatePicker with time
|
|
206
|
-
*
|
|
207
|
-
* **Files:**
|
|
208
|
-
* - `file` → FileInput - Single file upload
|
|
209
|
-
* - `fileArray` → FileInput multiple - Multiple files
|
|
210
|
-
* - `image` → ImageInput - Single image with preview
|
|
211
|
-
* - `imageArray` → ImageInput multiple - Image gallery
|
|
212
|
-
*
|
|
213
|
-
* **Other:**
|
|
214
|
-
* - `icon` → IconPicker - Icon selection from library
|
|
215
|
-
*
|
|
216
|
-
* @see {@link FieldOptions} for select/multiselect configuration
|
|
217
|
-
* @see {@link FieldStorageConfig} for file/image configuration
|
|
218
|
-
*/
|
|
219
|
-
type InputType = 'text' | 'email' | 'password' | 'url' | 'tel' | 'number' | 'decimal' | 'textarea' | 'markdown' | 'json' | 'select' | 'multiselect' | 'transfer' | 'tags' | 'checkbox' | 'switch' | 'date' | 'datetime' | 'file' | 'fileArray' | 'image' | 'imageArray' | 'icon';
|
|
220
|
-
/**
|
|
221
|
-
* Database column configuration for Knex migrations.
|
|
222
|
-
*
|
|
223
|
-
* @example
|
|
224
|
-
* ```typescript
|
|
225
|
-
* // String with max length
|
|
226
|
-
* db: { type: 'string', size: 100, nullable: false }
|
|
227
|
-
*
|
|
228
|
-
* // Decimal with precision
|
|
229
|
-
* db: { type: 'decimal', precision: [10, 2] }
|
|
230
|
-
*
|
|
231
|
-
* // UUID with auto-generation
|
|
232
|
-
* db: { type: 'uuid', defaultFn: 'uuid' }
|
|
233
|
-
*
|
|
234
|
-
* // Virtual field (no column in DB)
|
|
235
|
-
* db: { type: 'array', virtual: true }
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
interface FieldDbConfig {
|
|
239
|
-
/** Column type for the database */
|
|
240
|
-
type: DbType;
|
|
241
|
-
/** If true, no database column is created (for computed/virtual fields like role_ids) */
|
|
242
|
-
virtual?: boolean;
|
|
243
|
-
/** String length for VARCHAR columns */
|
|
244
|
-
size?: number;
|
|
245
|
-
/** Precision and scale for DECIMAL: [precision, scale] e.g., [10, 2] for DECIMAL(10,2) */
|
|
246
|
-
precision?: [number, number];
|
|
247
|
-
/** Allow NULL values. Inferred from `required` if not specified */
|
|
248
|
-
nullable?: boolean;
|
|
249
|
-
/** Create unique constraint on this column */
|
|
250
|
-
unique?: boolean;
|
|
251
|
-
/** Static default value for the column */
|
|
252
|
-
default?: unknown;
|
|
253
|
-
/** Database function for default value: 'now' for timestamps, 'uuid' for auto-generated UUIDs */
|
|
254
|
-
defaultFn?: 'now' | 'uuid';
|
|
255
|
-
/** Create index on this column for faster queries */
|
|
256
|
-
index?: boolean;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Foreign key relationship configuration.
|
|
260
|
-
*
|
|
261
|
-
* Defines how this field references another table in the database.
|
|
262
|
-
* Used for both migrations (FK constraint) and UI (dropdown options).
|
|
263
|
-
*
|
|
264
|
-
* @example
|
|
265
|
-
* ```typescript
|
|
266
|
-
* // Basic relation to users table
|
|
267
|
-
* relation: { table: 'users' }
|
|
268
|
-
*
|
|
269
|
-
* // With cascade delete
|
|
270
|
-
* relation: { table: 'categories', onDelete: 'CASCADE' }
|
|
271
|
-
*
|
|
272
|
-
* // Reference non-id column
|
|
273
|
-
* relation: { table: 'countries', column: 'code' }
|
|
274
|
-
* ```
|
|
275
|
-
*/
|
|
276
|
-
interface FieldRelation {
|
|
277
|
-
/** Target table name */
|
|
278
|
-
table: string;
|
|
279
|
-
/** Target column name (default: 'id') */
|
|
280
|
-
column?: string;
|
|
281
|
-
/** Action when referenced row is deleted */
|
|
282
|
-
onDelete?: 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'NO ACTION';
|
|
283
|
-
/** Action when referenced row's key is updated */
|
|
284
|
-
onUpdate?: 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'NO ACTION';
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Validation rules for runtime and form validation.
|
|
288
|
-
*
|
|
289
|
-
* Used to generate Zod schemas for API validation and
|
|
290
|
-
* form validation rules in the UI.
|
|
291
|
-
*
|
|
292
|
-
* @example
|
|
293
|
-
* ```typescript
|
|
294
|
-
* // String length constraints
|
|
295
|
-
* validation: { min: 1, max: 200 }
|
|
296
|
-
*
|
|
297
|
-
* // Email format
|
|
298
|
-
* validation: { format: 'email' }
|
|
299
|
-
*
|
|
300
|
-
* // Regex pattern
|
|
301
|
-
* validation: { pattern: '^[a-z0-9-]+$' }
|
|
302
|
-
*
|
|
303
|
-
* // Enum values
|
|
304
|
-
* validation: { enum: ['draft', 'published', 'archived'] }
|
|
305
|
-
* ```
|
|
306
|
-
*/
|
|
307
|
-
interface FieldValidationConfig {
|
|
308
|
-
/** Minimum value (numbers) or minimum length (strings) */
|
|
309
|
-
min?: number;
|
|
310
|
-
/** Maximum value (numbers) or maximum length (strings) */
|
|
311
|
-
max?: number;
|
|
312
|
-
/** Regular expression pattern for validation */
|
|
313
|
-
pattern?: string;
|
|
314
|
-
/** Predefined format validation */
|
|
315
|
-
format?: 'email' | 'url' | 'uuid' | 'slug';
|
|
316
|
-
/** Allowed values (creates enum constraint) */
|
|
317
|
-
enum?: string[];
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Configuration for select, multiselect, and dropdown fields.
|
|
321
|
-
*
|
|
322
|
-
* Options can be loaded from:
|
|
323
|
-
* - Static array defined in the field
|
|
324
|
-
* - Dynamic endpoint (API call)
|
|
325
|
-
* - Related entity via `relation` config
|
|
326
|
-
*
|
|
327
|
-
* @example
|
|
328
|
-
* ```typescript
|
|
329
|
-
* // Static options
|
|
330
|
-
* options: {
|
|
331
|
-
* static: [
|
|
332
|
-
* { value: 'draft', label: { en: 'Draft', es: 'Borrador' } },
|
|
333
|
-
* { value: 'published', label: { en: 'Published', es: 'Publicado' } }
|
|
334
|
-
* ]
|
|
335
|
-
* }
|
|
336
|
-
*
|
|
337
|
-
* // Dynamic from API
|
|
338
|
-
* options: {
|
|
339
|
-
* endpoint: '/api/categories',
|
|
340
|
-
* valueField: 'id',
|
|
341
|
-
* labelField: 'name'
|
|
342
|
-
* }
|
|
343
|
-
*
|
|
344
|
-
* // Allow creating new options (combobox)
|
|
345
|
-
* options: { allowCreate: true, endpoint: '/api/tags' }
|
|
346
|
-
* ```
|
|
347
|
-
*/
|
|
348
|
-
interface FieldOptions {
|
|
349
|
-
/** API endpoint for dynamic options (GET request) */
|
|
350
|
-
endpoint?: string;
|
|
351
|
-
/** Field to use as option value (default: 'id') */
|
|
352
|
-
valueField?: string;
|
|
353
|
-
/** Field to use as option label (default: 'name') */
|
|
354
|
-
labelField?: string;
|
|
355
|
-
/** Static list of options */
|
|
356
|
-
static?: Array<{
|
|
357
|
-
value: string;
|
|
358
|
-
label: LocalizedString;
|
|
359
|
-
}>;
|
|
360
|
-
/** Allow creating new options on-the-fly (renders combobox) */
|
|
361
|
-
allowCreate?: boolean;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Storage configuration for file and image upload fields.
|
|
365
|
-
*
|
|
366
|
-
* Controls upload behavior, file validation, and image processing.
|
|
367
|
-
*
|
|
368
|
-
* @example
|
|
369
|
-
* ```typescript
|
|
370
|
-
* // Image with thumbnails
|
|
371
|
-
* storage: {
|
|
372
|
-
* accept: 'image/*',
|
|
373
|
-
* maxSize: 5 * 1024 * 1024, // 5MB
|
|
374
|
-
* folder: 'avatars',
|
|
375
|
-
* thumbnails: [{ width: 100, height: 100 }, { width: 300, height: 300 }]
|
|
376
|
-
* }
|
|
377
|
-
*
|
|
378
|
-
* // PDF documents with deduplication
|
|
379
|
-
* storage: {
|
|
380
|
-
* accept: 'application/pdf',
|
|
381
|
-
* maxSize: 20 * 1024 * 1024, // 20MB
|
|
382
|
-
* folder: 'documents',
|
|
383
|
-
* dedupe: true
|
|
384
|
-
* }
|
|
385
|
-
*
|
|
386
|
-
* // Multiple images
|
|
387
|
-
* storage: {
|
|
388
|
-
* accept: 'image/*',
|
|
389
|
-
* maxFiles: 10,
|
|
390
|
-
* folder: 'gallery'
|
|
391
|
-
* }
|
|
392
|
-
* ```
|
|
393
|
-
*/
|
|
394
|
-
interface FieldStorageConfig {
|
|
395
|
-
/** Accepted MIME types (e.g., 'image/*', 'application/pdf', 'image/png,image/jpeg') */
|
|
396
|
-
accept?: string;
|
|
397
|
-
/** Maximum file size in bytes (default: 10MB = 10485760) */
|
|
398
|
-
maxSize?: number;
|
|
399
|
-
/** Maximum number of files for array types (fileArray, imageArray) */
|
|
400
|
-
maxFiles?: number;
|
|
401
|
-
/** Folder/prefix in storage bucket (e.g., 'avatars', 'documents/contracts') */
|
|
402
|
-
folder?: string;
|
|
403
|
-
/** Thumbnail sizes to auto-generate for images. Creates resized versions on upload */
|
|
404
|
-
thumbnails?: Array<{
|
|
405
|
-
width: number;
|
|
406
|
-
height: number;
|
|
407
|
-
}>;
|
|
408
|
-
/** Enable SHA256 hash deduplication - if identical file exists, reuse it instead of uploading again */
|
|
409
|
-
dedupe?: boolean;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Field metadata for UI behavior and data operations.
|
|
413
|
-
*
|
|
414
|
-
* Controls how the field behaves in tables, forms, exports, and searches.
|
|
415
|
-
*
|
|
416
|
-
* @example
|
|
417
|
-
* ```typescript
|
|
418
|
-
* // Searchable and sortable title field
|
|
419
|
-
* meta: { searchable: true, sortable: true, exportable: true }
|
|
420
|
-
*
|
|
421
|
-
* // Audit field (auto-populated)
|
|
422
|
-
* meta: { auditField: 'created_by', showInForm: false }
|
|
423
|
-
*
|
|
424
|
-
* // Show only in edit form, not create
|
|
425
|
-
* meta: { showInForm: 'edit' }
|
|
426
|
-
*
|
|
427
|
-
* // Hidden from tables but visible in forms
|
|
428
|
-
* meta: { showInDisplay: false }
|
|
429
|
-
* ```
|
|
430
|
-
*/
|
|
431
|
-
interface FieldMeta {
|
|
432
|
-
/** Enable sorting by this field in table columns */
|
|
433
|
-
sortable?: boolean;
|
|
434
|
-
/** Include this field in search/filter operations */
|
|
435
|
-
searchable?: boolean;
|
|
436
|
-
/** Include this field in CSV/Excel exports */
|
|
437
|
-
exportable?: boolean;
|
|
438
|
-
/** Mark as audit field (auto-populated with user ID on create/update) */
|
|
439
|
-
auditField?: 'created_by' | 'updated_by';
|
|
440
|
-
/** Show in display components (Table, List, Masonry). Default: true */
|
|
441
|
-
showInDisplay?: boolean;
|
|
442
|
-
/** Show in forms. Use 'create'/'edit' for conditional display. Default: true */
|
|
443
|
-
showInForm?: boolean | 'create' | 'edit';
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* Conditional boolean - can be static or evaluated at runtime based on record
|
|
447
|
-
* Functions are evaluated in backend before serializing to DTO
|
|
448
|
-
*/
|
|
449
|
-
type ConditionalBoolean = boolean | ((record: Record<string, unknown>) => boolean);
|
|
450
|
-
/**
|
|
451
|
-
* Complete field definition for entity fields.
|
|
452
|
-
*
|
|
453
|
-
* This is the core building block for defining entity schemas in Nexus.
|
|
454
|
-
* Contains all the information needed for:
|
|
455
|
-
* - **UI**: Form inputs, table columns, list displays
|
|
456
|
-
* - **Database**: Knex migrations, column types, constraints
|
|
457
|
-
* - **Validation**: Zod schemas, runtime validation rules
|
|
458
|
-
*
|
|
459
|
-
* @example
|
|
460
|
-
* ```typescript
|
|
461
|
-
* const titleField: FieldDefinition = {
|
|
462
|
-
* name: 'title',
|
|
463
|
-
* label: { en: 'Title', es: 'Título' },
|
|
464
|
-
* input: 'text',
|
|
465
|
-
* required: true,
|
|
466
|
-
* db: { type: 'string', nullable: false },
|
|
467
|
-
* validation: { min: 1, max: 200 }
|
|
468
|
-
* }
|
|
469
|
-
* ```
|
|
470
|
-
*
|
|
471
|
-
* @see {@link FieldDefinitionDTO} for the serializable API response version
|
|
472
|
-
* @see {@link EntityDefinition} for the parent entity structure
|
|
473
|
-
*/
|
|
474
|
-
interface FieldDefinition {
|
|
475
|
-
/**
|
|
476
|
-
* Field identifier used as the database column name.
|
|
477
|
-
* Should be snake_case for database compatibility.
|
|
478
|
-
* @example 'title', 'created_at', 'author_id'
|
|
479
|
-
*/
|
|
480
|
-
name: string;
|
|
481
|
-
/**
|
|
482
|
-
* Human-readable label displayed in forms, tables, and lists.
|
|
483
|
-
* Supports localization with `{ en: '...', es: '...' }` format.
|
|
484
|
-
*/
|
|
485
|
-
label: LocalizedString;
|
|
486
|
-
/**
|
|
487
|
-
* Input type that determines which UI component renders this field.
|
|
488
|
-
* @see {@link InputType} for all available input types
|
|
489
|
-
*/
|
|
490
|
-
input: InputType;
|
|
491
|
-
/**
|
|
492
|
-
* Placeholder text shown in empty input fields.
|
|
493
|
-
* Supports localization.
|
|
494
|
-
*/
|
|
495
|
-
placeholder?: LocalizedString;
|
|
496
|
-
/**
|
|
497
|
-
* Help text displayed below the input field.
|
|
498
|
-
* Use for additional guidance or format hints.
|
|
499
|
-
*/
|
|
500
|
-
hint?: LocalizedString;
|
|
501
|
-
/**
|
|
502
|
-
* Controls field visibility in the UI.
|
|
503
|
-
* - `true`: Always hidden
|
|
504
|
-
* - `false`: Always visible
|
|
505
|
-
* - `(record) => boolean`: Dynamic based on record values
|
|
506
|
-
* @example hidden: (record) => record.type === 'draft'
|
|
507
|
-
*/
|
|
508
|
-
hidden?: ConditionalBoolean;
|
|
509
|
-
/**
|
|
510
|
-
* Controls whether the field is read-only in forms.
|
|
511
|
-
* - `true`: Always disabled
|
|
512
|
-
* - `false`: Always editable
|
|
513
|
-
* - `(record) => boolean`: Dynamic based on record values
|
|
514
|
-
* @example disabled: (record) => record.status === 'published'
|
|
515
|
-
*/
|
|
516
|
-
disabled?: ConditionalBoolean;
|
|
517
|
-
/**
|
|
518
|
-
* Marks the field as required for form validation.
|
|
519
|
-
* - `true`: Always required
|
|
520
|
-
* - `false`: Always optional
|
|
521
|
-
* - `(record) => boolean`: Dynamic based on record values
|
|
522
|
-
* @example required: (record) => record.is_featured === true
|
|
523
|
-
*/
|
|
524
|
-
required?: ConditionalBoolean;
|
|
525
|
-
/**
|
|
526
|
-
* Default value used when creating new records.
|
|
527
|
-
* Type should match the field's expected data type.
|
|
528
|
-
*/
|
|
529
|
-
defaultValue?: unknown;
|
|
530
|
-
/**
|
|
531
|
-
* Database column configuration for Knex migrations.
|
|
532
|
-
* Optional for virtual fields, actions, or external data sources.
|
|
533
|
-
* @see {@link FieldDbConfig}
|
|
534
|
-
*/
|
|
535
|
-
db?: FieldDbConfig;
|
|
536
|
-
/**
|
|
537
|
-
* Foreign key relationship configuration.
|
|
538
|
-
* Defines how this field relates to another entity's table.
|
|
539
|
-
* @see {@link FieldRelation}
|
|
540
|
-
*/
|
|
541
|
-
relation?: FieldRelation;
|
|
542
|
-
/**
|
|
543
|
-
* Validation rules applied during form submission and API requests.
|
|
544
|
-
* Used to generate Zod schemas for runtime validation.
|
|
545
|
-
* @see {@link FieldValidationConfig}
|
|
546
|
-
*/
|
|
547
|
-
validation?: FieldValidationConfig;
|
|
548
|
-
/**
|
|
549
|
-
* Configuration for select inputs and relationship dropdowns.
|
|
550
|
-
* Defines static options or dynamic data sources.
|
|
551
|
-
* @see {@link FieldOptions}
|
|
552
|
-
*/
|
|
553
|
-
options?: FieldOptions;
|
|
554
|
-
/**
|
|
555
|
-
* Storage configuration for file and image inputs.
|
|
556
|
-
* Defines upload constraints, folders, and thumbnail generation.
|
|
557
|
-
* @see {@link FieldStorageConfig}
|
|
558
|
-
*/
|
|
559
|
-
storage?: FieldStorageConfig;
|
|
560
|
-
/**
|
|
561
|
-
* Additional metadata for categorization and UI organization.
|
|
562
|
-
* @see {@link FieldMeta}
|
|
563
|
-
*/
|
|
564
|
-
meta?: FieldMeta;
|
|
565
|
-
/**
|
|
566
|
-
* Additional props passed directly to the form input component.
|
|
567
|
-
* Allows customization of Naive UI component behavior.
|
|
568
|
-
* @example inputProps: { rows: 5, showCount: true }
|
|
569
|
-
*/
|
|
570
|
-
inputProps?: Record<string, unknown>;
|
|
571
|
-
/**
|
|
572
|
-
* Additional props passed to display components (table columns, list items).
|
|
573
|
-
* Use `order` prop to control field position in tables.
|
|
574
|
-
* @example displayProps: { order: 1, width: 200 }
|
|
575
|
-
*/
|
|
576
|
-
displayProps?: Record<string, unknown>;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Composite table index definition for database migrations.
|
|
580
|
-
*
|
|
581
|
-
* Creates multi-column indexes for optimized queries.
|
|
582
|
-
*
|
|
583
|
-
* @example
|
|
584
|
-
* ```typescript
|
|
585
|
-
* // Composite index for frequent queries
|
|
586
|
-
* indexes: [{ columns: ['tenant_id', 'created_at'] }]
|
|
587
|
-
*
|
|
588
|
-
* // Unique constraint on multiple columns
|
|
589
|
-
* indexes: [{ columns: ['user_id', 'role_id'], unique: true }]
|
|
590
|
-
* ```
|
|
591
|
-
*/
|
|
592
|
-
interface EntityIndex {
|
|
593
|
-
/** Column names to include in the composite index */
|
|
594
|
-
columns: string[];
|
|
595
|
-
/** Create unique constraint on the column combination */
|
|
596
|
-
unique?: boolean;
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Standard CASL authorization actions.
|
|
600
|
-
*
|
|
601
|
-
* - `manage` - Full access (superuser wildcard, includes all actions)
|
|
602
|
-
* - `create` - Create new records
|
|
603
|
-
* - `read` - Read/view records
|
|
604
|
-
* - `update` - Modify existing records
|
|
605
|
-
* - `delete` - Remove records
|
|
606
|
-
* - `execute` - Execute actions/operations on records
|
|
607
|
-
*
|
|
608
|
-
* @see {@link RolePermission} for role-based permission configuration
|
|
609
|
-
* @see https://casl.js.org/v6/en/guide/intro
|
|
610
|
-
*/
|
|
611
|
-
type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'execute';
|
|
612
|
-
/**
|
|
613
|
-
* Ownership condition for CASL permissions.
|
|
614
|
-
*
|
|
615
|
-
* Automatically generates conditions that restrict access to records
|
|
616
|
-
* owned by the current user.
|
|
617
|
-
*
|
|
618
|
-
* @example
|
|
619
|
-
* ```typescript
|
|
620
|
-
* // Users can only edit their own posts
|
|
621
|
-
* ownership: { field: 'author_id' }
|
|
622
|
-
* // Generates: { author_id: user.id }
|
|
623
|
-
*
|
|
624
|
-
* // Match against user's email instead of id
|
|
625
|
-
* ownership: { field: 'owner_email', userField: 'email' }
|
|
626
|
-
* // Generates: { owner_email: user.email }
|
|
627
|
-
* ```
|
|
628
|
-
*/
|
|
629
|
-
interface OwnershipCondition {
|
|
630
|
-
/** Entity field that contains the owner reference (e.g., 'author_id', 'created_by') */
|
|
631
|
-
field: string;
|
|
632
|
-
/** User property to compare against (default: 'id'). Use 'email' for email-based ownership */
|
|
633
|
-
userField?: string;
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* CASL permission definition for a specific role.
|
|
637
|
-
*
|
|
638
|
-
* Defines what actions a role can perform on an entity,
|
|
639
|
-
* optionally with conditions and field restrictions.
|
|
640
|
-
*
|
|
641
|
-
* @example
|
|
642
|
-
* ```typescript
|
|
643
|
-
* // Editor can read and update, but not delete
|
|
644
|
-
* EDITOR: {
|
|
645
|
-
* actions: ['read', 'update'],
|
|
646
|
-
* fields: ['title', 'content', 'status']
|
|
647
|
-
* }
|
|
648
|
-
*
|
|
649
|
-
* // Viewer can only read published items
|
|
650
|
-
* VIEWER: {
|
|
651
|
-
* actions: ['read'],
|
|
652
|
-
* conditions: { status: 'published' }
|
|
653
|
-
* }
|
|
654
|
-
*
|
|
655
|
-
* // Deny delete for everyone except admin
|
|
656
|
-
* EDITOR: {
|
|
657
|
-
* actions: ['delete'],
|
|
658
|
-
* inverted: true
|
|
659
|
-
* }
|
|
660
|
-
* ```
|
|
661
|
-
*/
|
|
662
|
-
interface RolePermission {
|
|
663
|
-
/** Actions this role can perform on the entity */
|
|
664
|
-
actions: CaslAction[];
|
|
665
|
-
/** CASL conditions to filter which records the permission applies to */
|
|
666
|
-
conditions?: Record<string, unknown>;
|
|
667
|
-
/** Restrict permission to specific fields. null = all fields allowed */
|
|
668
|
-
fields?: string[] | null;
|
|
669
|
-
/** If true, this is a denial rule (cannot) instead of allow (can) */
|
|
670
|
-
inverted?: boolean;
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* CASL authorization configuration for an entity.
|
|
674
|
-
*
|
|
675
|
-
* Defines role-based access control (RBAC) using CASL.
|
|
676
|
-
* Permissions are evaluated at runtime for each request.
|
|
677
|
-
*
|
|
678
|
-
* @example
|
|
679
|
-
* ```typescript
|
|
680
|
-
* casl: {
|
|
681
|
-
* subject: 'BlogPost',
|
|
682
|
-
* ownership: { field: 'author_id' },
|
|
683
|
-
* permissions: {
|
|
684
|
-
* ADMIN: { actions: ['manage'] },
|
|
685
|
-
* EDITOR: { actions: ['read', 'create', 'update'] },
|
|
686
|
-
* AUTHOR: { actions: ['read', 'update', 'delete'] }, // with ownership
|
|
687
|
-
* VIEWER: { actions: ['read'], conditions: { status: 'published' } }
|
|
688
|
-
* },
|
|
689
|
-
* sensitiveFields: ['internal_notes', 'analytics']
|
|
690
|
-
* }
|
|
691
|
-
* ```
|
|
692
|
-
*
|
|
693
|
-
* @see https://casl.js.org/v6/en/guide/intro
|
|
694
|
-
*/
|
|
695
|
-
interface EntityCaslConfig {
|
|
696
|
-
/** CASL subject name. Default: inferred from table (e.g., 'cms_posts' → 'CmsPost') */
|
|
697
|
-
subject?: string;
|
|
698
|
-
/** Ownership configuration for automatic user-based filtering */
|
|
699
|
-
ownership?: OwnershipCondition;
|
|
700
|
-
/** Role-based permissions. Keys are role names (ADMIN, EDITOR, VIEWER, etc.) */
|
|
701
|
-
permissions?: Record<string, RolePermission>;
|
|
702
|
-
/** Fields excluded from 'read' permission unless explicitly granted. Use for PII or internal data */
|
|
703
|
-
sensitiveFields?: string[];
|
|
704
|
-
}
|
|
705
|
-
/**
|
|
706
|
-
* Action linked to an entity record.
|
|
707
|
-
* Defines operations that act on existing records.
|
|
708
|
-
*
|
|
709
|
-
* Generated route: GET|POST /{entityRoutePrefix}/{key}/:id
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
712
|
-
* ```typescript
|
|
713
|
-
* actions: [{
|
|
714
|
-
* key: 'download',
|
|
715
|
-
* label: 'Download File',
|
|
716
|
-
* icon: 'mdi:download',
|
|
717
|
-
* method: 'GET',
|
|
718
|
-
* select: ['id', 'filename', 'mimetype'],
|
|
719
|
-
* handler: async (ctx, input, _req, res) => {
|
|
720
|
-
* const { _record: file, _authUserId } = input
|
|
721
|
-
* // file and _authUserId are injected automatically
|
|
722
|
-
* }
|
|
723
|
-
* }]
|
|
724
|
-
* ```
|
|
725
|
-
*/
|
|
726
|
-
interface EntityAction {
|
|
727
|
-
/** Unique key for the route (e.g., 'download' → /download/:id) */
|
|
728
|
-
key: string;
|
|
729
|
-
/** Label for the UI */
|
|
730
|
-
label: LocalizedString;
|
|
731
|
-
/** Iconify icon (default: 'mdi:play') */
|
|
732
|
-
icon?: string;
|
|
733
|
-
/** HTTP method (default: 'POST') */
|
|
734
|
-
method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
735
|
-
/** Fields to select from the record (default: all) */
|
|
736
|
-
select?: string[];
|
|
737
|
-
/** Body validation schema (optional) */
|
|
738
|
-
inputSchema?: ValidationSchema;
|
|
739
|
-
/** Output schema (documentation) */
|
|
740
|
-
outputSchema?: ValidationSchema;
|
|
741
|
-
/** Custom middleware (e.g., multer) */
|
|
742
|
-
middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
|
|
743
|
-
/**
|
|
744
|
-
* Handler - receives _record and _authUserId automatically.
|
|
745
|
-
* @param ctx - Module context
|
|
746
|
-
* @param input - Validated body + _record + _authUserId
|
|
747
|
-
* @param req - Express Request
|
|
748
|
-
* @param res - Express Response (for streams, custom headers)
|
|
749
|
-
*/
|
|
750
|
-
handler: (ctx: ModuleContext, input: unknown, req?: Request, res?: Response) => Promise<unknown>;
|
|
751
|
-
/**
|
|
752
|
-
* Skip authentication for this action.
|
|
753
|
-
* Use for public endpoints like thumbnails, previews, etc.
|
|
754
|
-
* @default false
|
|
755
|
-
*/
|
|
756
|
-
skipAuth?: boolean;
|
|
757
|
-
/**
|
|
758
|
-
* CASL permissions specific to this action.
|
|
759
|
-
* If not defined, it inherits permissions from the parent entity.
|
|
760
|
-
* Ignored if skipAuth is true.
|
|
761
|
-
*/
|
|
762
|
-
casl?: {
|
|
763
|
-
/** CASL action (default: 'execute') */
|
|
764
|
-
action?: string;
|
|
765
|
-
/** Additional conditions */
|
|
766
|
-
conditions?: Record<string, unknown>;
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Base properties shared by all EntityDefinition types.
|
|
771
|
-
*
|
|
772
|
-
* @internal This interface is not exported directly. Use specific entity types instead.
|
|
773
|
-
* @see {@link CollectionEntityDefinition}
|
|
774
|
-
* @see {@link SingleEntityDefinition}
|
|
775
|
-
* @see {@link ReferenceEntityDefinition}
|
|
776
|
-
*/
|
|
777
|
-
interface BaseEntityDefinition {
|
|
778
|
-
/** Database table name with module prefix (e.g., 'cms_posts', 'auth_users') */
|
|
779
|
-
table: string;
|
|
780
|
-
/** Display name for the UI (singular form) */
|
|
781
|
-
label: LocalizedString;
|
|
782
|
-
/** Display name for the UI (plural form, used in navigation menus and list headers) */
|
|
783
|
-
labelPlural?: LocalizedString;
|
|
784
|
-
/** Field definitions keyed by field name. See {@link FieldDefinition} */
|
|
785
|
-
fields: Record<string, FieldDefinition>;
|
|
786
|
-
/** CASL authorization configuration for role-based access control */
|
|
787
|
-
casl?: EntityCaslConfig;
|
|
788
|
-
/** API route prefix. Default: inferred from table name (e.g., 'cms_posts' → '/cms-posts') */
|
|
789
|
-
routePrefix?: string;
|
|
790
|
-
/** UI sidebar ordering. Lower values appear first (default: 999) */
|
|
791
|
-
order?: number;
|
|
792
|
-
/** UI display mode: 'table' for data grids, 'list' for cards, 'masonry' for image galleries */
|
|
793
|
-
displayMode?: DisplayMode;
|
|
794
|
-
/** If true, hides from UI sidebar but entity remains accessible via API */
|
|
795
|
-
hidden?: boolean;
|
|
796
|
-
}
|
|
797
|
-
/**
|
|
798
|
-
* Collection entity - the most common entity type with full CRUD operations.
|
|
799
|
-
*
|
|
800
|
-
* Use for business data like users, posts, orders, products, etc.
|
|
801
|
-
* This is the default type when `type` is not specified.
|
|
802
|
-
*
|
|
803
|
-
* @example
|
|
804
|
-
* ```typescript
|
|
805
|
-
* const posts: CollectionEntityDefinition = {
|
|
806
|
-
* type: 'collection', // optional, default
|
|
807
|
-
* table: 'cms_posts',
|
|
808
|
-
* label: { en: 'Post', es: 'Publicación' },
|
|
809
|
-
* labelField: 'title',
|
|
810
|
-
* timestamps: true,
|
|
811
|
-
* audit: true,
|
|
812
|
-
* softDelete: true,
|
|
813
|
-
* fields: { ... }
|
|
814
|
-
* }
|
|
815
|
-
* ```
|
|
816
|
-
*/
|
|
817
|
-
interface CollectionEntityDefinition extends BaseEntityDefinition {
|
|
818
|
-
/** Entity type. Default: 'collection' */
|
|
819
|
-
type?: 'collection';
|
|
820
|
-
/** Field used as display label in dropdowns, references, and breadcrumbs (e.g., 'name', 'title') */
|
|
821
|
-
labelField: string;
|
|
822
|
-
/** Auto-add created_at and updated_at timestamp columns */
|
|
823
|
-
timestamps?: boolean;
|
|
824
|
-
/** Auto-add created_by and updated_by user reference columns */
|
|
825
|
-
audit?: boolean;
|
|
826
|
-
/** Composite database indexes for query optimization */
|
|
827
|
-
indexes?: EntityIndex[];
|
|
828
|
-
/** Use soft delete (deleted_at column) instead of physical DELETE */
|
|
829
|
-
softDelete?: boolean;
|
|
830
|
-
/** Custom actions that operate on entity records (e.g., download, publish, archive) */
|
|
831
|
-
actions?: EntityAction[];
|
|
832
|
-
/** Show "Create" button in UI. Set false for entities created only via API/actions */
|
|
833
|
-
allowCreate?: boolean;
|
|
834
|
-
/** Allow editing in UI. Set false for read-only display entities */
|
|
835
|
-
allowEdit?: boolean;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Singleton entity - stores a single record in the shared sys_settings table.
|
|
839
|
-
*
|
|
840
|
-
* Perfect for application-wide configuration like site settings, SMTP config,
|
|
841
|
-
* feature flags, etc. Data is stored as JSON in a key-value format.
|
|
842
|
-
*
|
|
843
|
-
* @example
|
|
844
|
-
* ```typescript
|
|
845
|
-
* const siteConfig: SingleEntityDefinition = {
|
|
846
|
-
* type: 'single',
|
|
847
|
-
* key: 'site_config',
|
|
848
|
-
* label: { en: 'Site Configuration', es: 'Configuración del Sitio' },
|
|
849
|
-
* defaults: { siteName: 'My App', maintenanceMode: false },
|
|
850
|
-
* fields: {
|
|
851
|
-
* siteName: { name: 'siteName', label: 'Site Name', input: 'text', ... },
|
|
852
|
-
* logo: { name: 'logo', label: 'Logo', input: 'image', ... }
|
|
853
|
-
* }
|
|
854
|
-
* }
|
|
855
|
-
* // Stored in sys_settings: { key: 'site_config', value: '{"siteName":"My App","logo":"..."}' }
|
|
856
|
-
* ```
|
|
857
|
-
*/
|
|
858
|
-
interface SingleEntityDefinition {
|
|
859
|
-
/** Entity type identifier */
|
|
860
|
-
type: 'single';
|
|
861
|
-
/** Unique key in sys_settings table (e.g., 'site_config', 'smtp_settings', 'feature_flags') */
|
|
862
|
-
key: string;
|
|
863
|
-
/** Display name for the UI (singular form) */
|
|
864
|
-
label: LocalizedString;
|
|
865
|
-
/** Display name for the UI (plural form) */
|
|
866
|
-
labelPlural?: LocalizedString;
|
|
867
|
-
/** Field definitions describing the JSON structure */
|
|
868
|
-
fields: Record<string, FieldDefinition>;
|
|
869
|
-
/** Default values applied when the setting is first accessed */
|
|
870
|
-
defaults?: Record<string, unknown>;
|
|
871
|
-
/** CASL authorization for read/update operations */
|
|
872
|
-
casl?: EntityCaslConfig;
|
|
873
|
-
/** API route prefix. Default: inferred from key */
|
|
874
|
-
routePrefix?: string;
|
|
875
|
-
/** Custom actions for this singleton (e.g., 'test-smtp', 'reset-defaults') */
|
|
876
|
-
actions?: EntityAction[];
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* Reference entity - read-only catalogs for lookups and dropdowns.
|
|
880
|
-
*
|
|
881
|
-
* Use for relatively static data like countries, currencies, categories, statuses.
|
|
882
|
-
* Data can be seeded from code and optionally edited by admins.
|
|
883
|
-
*
|
|
884
|
-
* @example
|
|
885
|
-
* ```typescript
|
|
886
|
-
* const countries: ReferenceEntityDefinition = {
|
|
887
|
-
* type: 'reference',
|
|
888
|
-
* table: 'ref_countries',
|
|
889
|
-
* label: { en: 'Country', es: 'País' },
|
|
890
|
-
* labelField: 'name',
|
|
891
|
-
* seed: [
|
|
892
|
-
* { code: 'US', name: 'United States' },
|
|
893
|
-
* { code: 'ES', name: 'Spain' }
|
|
894
|
-
* ],
|
|
895
|
-
* allowAdminEdit: true,
|
|
896
|
-
* fields: { ... }
|
|
897
|
-
* }
|
|
898
|
-
* ```
|
|
899
|
-
*/
|
|
900
|
-
interface ReferenceEntityDefinition extends BaseEntityDefinition {
|
|
901
|
-
/** Entity type identifier */
|
|
902
|
-
type: 'reference';
|
|
903
|
-
/** Field used as display label in dropdowns (e.g., 'name', 'title') */
|
|
904
|
-
labelField: string;
|
|
905
|
-
/** Auto-add created_at and updated_at timestamp columns */
|
|
906
|
-
timestamps?: boolean;
|
|
907
|
-
/** Composite database indexes */
|
|
908
|
-
indexes?: EntityIndex[];
|
|
909
|
-
/** Initial seed data inserted on first migration/startup */
|
|
910
|
-
seed?: Array<Record<string, unknown>>;
|
|
911
|
-
/** Allow admin users to edit reference data. Default: false (read-only) */
|
|
912
|
-
allowAdminEdit?: boolean;
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Event entity - append-only logs for audit trails and analytics.
|
|
916
|
-
*
|
|
917
|
-
* Records can only be created (appended), never updated or deleted.
|
|
918
|
-
* Supports automatic retention policies to prevent unbounded growth.
|
|
919
|
-
*
|
|
920
|
-
* @example
|
|
921
|
-
* ```typescript
|
|
922
|
-
* const auditLogs: EventEntityDefinition = {
|
|
923
|
-
* type: 'event',
|
|
924
|
-
* table: 'sys_audit_logs',
|
|
925
|
-
* label: { en: 'Audit Log', es: 'Log de Auditoría' },
|
|
926
|
-
* labelField: 'action',
|
|
927
|
-
* timestamps: true,
|
|
928
|
-
* retention: { days: 90, maxRows: 100000 },
|
|
929
|
-
* fields: {
|
|
930
|
-
* action: { name: 'action', label: 'Action', input: 'text', ... },
|
|
931
|
-
* user_id: { name: 'user_id', label: 'User', input: 'select', ... },
|
|
932
|
-
* payload: { name: 'payload', label: 'Data', input: 'json', ... }
|
|
933
|
-
* }
|
|
934
|
-
* }
|
|
935
|
-
* ```
|
|
936
|
-
*/
|
|
937
|
-
interface EventEntityDefinition extends BaseEntityDefinition {
|
|
938
|
-
/** Entity type identifier */
|
|
939
|
-
type: 'event';
|
|
940
|
-
/** Field used as display label in event lists (e.g., 'action', 'event_type') */
|
|
941
|
-
labelField: string;
|
|
942
|
-
/** Auto-add created_at column. Set false if you define timestamp fields explicitly */
|
|
943
|
-
timestamps?: boolean;
|
|
944
|
-
/** Automatic data retention to limit table growth */
|
|
945
|
-
retention?: {
|
|
946
|
-
/** Delete records older than N days */
|
|
947
|
-
days?: number;
|
|
948
|
-
/** Keep only the most recent N records (FIFO) */
|
|
949
|
-
maxRows?: number;
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
/**
|
|
953
|
-
* Action entity - standalone commands/operations without data persistence.
|
|
954
|
-
*
|
|
955
|
-
* Use for operations like sending emails, generating reports, triggering workflows.
|
|
956
|
-
* For actions that operate on existing records, use {@link EntityAction} instead.
|
|
957
|
-
*
|
|
958
|
-
* @example
|
|
959
|
-
* ```typescript
|
|
960
|
-
* const sendEmail: ActionEntityDefinition = {
|
|
961
|
-
* type: 'action',
|
|
962
|
-
* label: { en: 'Send Email', es: 'Enviar Email' },
|
|
963
|
-
* icon: 'mdi:email-send',
|
|
964
|
-
* fields: {
|
|
965
|
-
* to: { name: 'to', label: 'To', input: 'email', required: true },
|
|
966
|
-
* subject: { name: 'subject', label: 'Subject', input: 'text', required: true },
|
|
967
|
-
* body: { name: 'body', label: 'Body', input: 'markdown' }
|
|
968
|
-
* },
|
|
969
|
-
* handler: async (ctx, input) => {
|
|
970
|
-
* await ctx.services.mail.send(input)
|
|
971
|
-
* return { success: true }
|
|
972
|
-
* }
|
|
973
|
-
* }
|
|
974
|
-
* ```
|
|
975
|
-
*/
|
|
976
|
-
interface ActionEntityDefinition {
|
|
977
|
-
/** Entity type identifier */
|
|
978
|
-
type: 'action';
|
|
979
|
-
/** Display name for the action button/form */
|
|
980
|
-
label: LocalizedString;
|
|
981
|
-
/** Iconify icon identifier (default: 'mdi:play') */
|
|
982
|
-
icon?: string;
|
|
983
|
-
/** Form fields for action input. These don't create database columns */
|
|
984
|
-
fields: Record<string, FieldDefinition>;
|
|
985
|
-
/** Zod schema for validating input before execution */
|
|
986
|
-
inputSchema?: ValidationSchema;
|
|
987
|
-
/** Zod schema documenting the response structure */
|
|
988
|
-
outputSchema?: ValidationSchema;
|
|
989
|
-
/** HTTP method for the action endpoint (default: 'POST') */
|
|
990
|
-
method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
991
|
-
/** Custom middleware (e.g., multer for file uploads). Returns single or array of handlers */
|
|
992
|
-
middleware?: (ctx: ModuleContext) => RequestHandler | RequestHandler[];
|
|
993
|
-
/** Handler function that executes the action logic. Input includes _authUserId if authenticated */
|
|
994
|
-
handler?: (ctx: ModuleContext, input: unknown, req?: Request, res?: Response) => Promise<unknown>;
|
|
995
|
-
/** HTTP status code for success response. Default: 200, use 201 for resource creation */
|
|
996
|
-
successStatus?: number;
|
|
997
|
-
/** CASL authorization configuration */
|
|
998
|
-
casl?: EntityCaslConfig;
|
|
999
|
-
/** API route prefix for the action endpoint */
|
|
1000
|
-
routePrefix?: string;
|
|
1001
|
-
/** UI sidebar ordering. Lower values appear first (default: 999) */
|
|
1002
|
-
order?: number;
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* External entity - read-only data from external APIs.
|
|
1006
|
-
*
|
|
1007
|
-
* Use for integrating third-party services like Stripe customers, GitHub repos,
|
|
1008
|
-
* or any external REST API. Data is fetched on-demand through adapters.
|
|
1009
|
-
*
|
|
1010
|
-
* @example
|
|
1011
|
-
* ```typescript
|
|
1012
|
-
* const stripeCustomers: ExternalEntityDefinition = {
|
|
1013
|
-
* type: 'external',
|
|
1014
|
-
* label: { en: 'Stripe Customer', es: 'Cliente Stripe' },
|
|
1015
|
-
* labelField: 'email',
|
|
1016
|
-
* adapter: 'stripe',
|
|
1017
|
-
* source: { endpoint: 'customers' },
|
|
1018
|
-
* cache: { ttl: 300, invalidateOn: ['stripe.customer.*'] },
|
|
1019
|
-
* fields: {
|
|
1020
|
-
* id: { name: 'id', label: 'ID', input: 'text' },
|
|
1021
|
-
* email: { name: 'email', label: 'Email', input: 'email' },
|
|
1022
|
-
* name: { name: 'name', label: 'Name', input: 'text' }
|
|
1023
|
-
* }
|
|
1024
|
-
* }
|
|
1025
|
-
* ```
|
|
1026
|
-
*/
|
|
1027
|
-
interface ExternalEntityDefinition {
|
|
1028
|
-
/** Entity type identifier */
|
|
1029
|
-
type: 'external';
|
|
1030
|
-
/** Display name for the UI (singular form) */
|
|
1031
|
-
label: LocalizedString;
|
|
1032
|
-
/** Display name for the UI (plural form) */
|
|
1033
|
-
labelPlural?: LocalizedString;
|
|
1034
|
-
/** Field used as display label in lists and references */
|
|
1035
|
-
labelField: string;
|
|
1036
|
-
/** Field definitions mapping to the external API response structure */
|
|
1037
|
-
fields: Record<string, FieldDefinition>;
|
|
1038
|
-
/** Registered adapter name that handles API communication (e.g., 'stripe', 'github', 'salesforce') */
|
|
1039
|
-
adapter: string;
|
|
1040
|
-
/** Configuration passed to the adapter */
|
|
1041
|
-
source?: {
|
|
1042
|
-
/** API endpoint or resource path */
|
|
1043
|
-
endpoint?: string;
|
|
1044
|
-
/** Additional adapter-specific configuration */
|
|
1045
|
-
[key: string]: unknown;
|
|
1046
|
-
};
|
|
1047
|
-
/** Response caching to reduce API calls */
|
|
1048
|
-
cache?: {
|
|
1049
|
-
/** Cache time-to-live in seconds */
|
|
1050
|
-
ttl: number;
|
|
1051
|
-
/** Field used to generate unique cache keys */
|
|
1052
|
-
key?: string;
|
|
1053
|
-
/** Event patterns that invalidate cached data (e.g., ['stripe.customer.*']) */
|
|
1054
|
-
invalidateOn?: string[];
|
|
1055
|
-
};
|
|
1056
|
-
/** CASL authorization for read operations */
|
|
1057
|
-
casl?: EntityCaslConfig;
|
|
1058
|
-
/** API route prefix */
|
|
1059
|
-
routePrefix?: string;
|
|
1060
|
-
/** UI sidebar ordering (default: 999) */
|
|
1061
|
-
order?: number;
|
|
1062
|
-
/** UI display mode: 'table', 'list', or 'masonry' */
|
|
1063
|
-
displayMode?: DisplayMode;
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Virtual entity - data aggregation across multiple sources.
|
|
1067
|
-
*
|
|
1068
|
-
* Use to create unified views combining local entities, external APIs, or computed data.
|
|
1069
|
-
* Read-only, no persistence - data is resolved on each request.
|
|
1070
|
-
*
|
|
1071
|
-
* @example
|
|
1072
|
-
* ```typescript
|
|
1073
|
-
* const unifiedContacts: VirtualEntityDefinition = {
|
|
1074
|
-
* type: 'virtual',
|
|
1075
|
-
* label: { en: 'Contact', es: 'Contacto' },
|
|
1076
|
-
* labelField: 'name',
|
|
1077
|
-
* sources: ['crm_contacts', 'stripe_customers'],
|
|
1078
|
-
* resolver: (sources, ctx) => {
|
|
1079
|
-
* const contacts = sources.crm_contacts.map(c => ({ ...c, source: 'crm' }))
|
|
1080
|
-
* const customers = sources.stripe_customers.map(c => ({ ...c, source: 'stripe' }))
|
|
1081
|
-
* return [...contacts, ...customers]
|
|
1082
|
-
* },
|
|
1083
|
-
* fields: { ... }
|
|
1084
|
-
* }
|
|
1085
|
-
* ```
|
|
1086
|
-
*/
|
|
1087
|
-
interface VirtualEntityDefinition {
|
|
1088
|
-
/** Entity type identifier */
|
|
1089
|
-
type: 'virtual';
|
|
1090
|
-
/** Display name for the UI (singular form) */
|
|
1091
|
-
label: LocalizedString;
|
|
1092
|
-
/** Display name for the UI (plural form) */
|
|
1093
|
-
labelPlural?: LocalizedString;
|
|
1094
|
-
/** Field used as display label in lists and references */
|
|
1095
|
-
labelField: string;
|
|
1096
|
-
/** Unified field definitions for the combined schema */
|
|
1097
|
-
fields: Record<string, FieldDefinition>;
|
|
1098
|
-
/** Entity or table names to fetch data from */
|
|
1099
|
-
sources: string[];
|
|
1100
|
-
/** Custom function to combine and transform data from all sources */
|
|
1101
|
-
resolver?: (sources: Record<string, unknown[]>, ctx: ModuleContext) => unknown[] | Promise<unknown[]>;
|
|
1102
|
-
/** CASL authorization for read operations */
|
|
1103
|
-
casl?: EntityCaslConfig;
|
|
1104
|
-
/** API route prefix */
|
|
1105
|
-
routePrefix?: string;
|
|
1106
|
-
/** UI sidebar ordering (default: 999) */
|
|
1107
|
-
order?: number;
|
|
1108
|
-
/** UI display mode: 'table', 'list', or 'masonry' */
|
|
1109
|
-
displayMode?: DisplayMode;
|
|
1110
|
-
}
|
|
1111
|
-
/**
|
|
1112
|
-
* Computed entity - dynamically calculated data like KPIs, statistics, and metrics.
|
|
1113
|
-
*
|
|
1114
|
-
* Use for dashboards, reports, and analytics that derive from other data sources.
|
|
1115
|
-
* Read-only, computed on-demand with optional caching.
|
|
1116
|
-
*
|
|
1117
|
-
* @example
|
|
1118
|
-
* ```typescript
|
|
1119
|
-
* const salesMetrics: ComputedEntityDefinition = {
|
|
1120
|
-
* type: 'computed',
|
|
1121
|
-
* label: { en: 'Sales Metrics', es: 'Métricas de Ventas' },
|
|
1122
|
-
* isSingle: true, // returns single record with all metrics
|
|
1123
|
-
* cache: { ttl: 60, invalidateOn: ['db.orders.*'] },
|
|
1124
|
-
* compute: async (ctx) => [{
|
|
1125
|
-
* totalSales: await ctx.db('orders').sum('total'),
|
|
1126
|
-
* orderCount: await ctx.db('orders').count(),
|
|
1127
|
-
* avgOrderValue: await ctx.db('orders').avg('total')
|
|
1128
|
-
* }],
|
|
1129
|
-
* fields: {
|
|
1130
|
-
* totalSales: { name: 'totalSales', label: 'Total Sales', input: 'number' },
|
|
1131
|
-
* orderCount: { name: 'orderCount', label: 'Orders', input: 'number' },
|
|
1132
|
-
* avgOrderValue: { name: 'avgOrderValue', label: 'Avg Order', input: 'decimal' }
|
|
1133
|
-
* }
|
|
1134
|
-
* }
|
|
1135
|
-
* ```
|
|
1136
|
-
*/
|
|
1137
|
-
interface ComputedEntityDefinition {
|
|
1138
|
-
/** Entity type identifier */
|
|
1139
|
-
type: 'computed';
|
|
1140
|
-
/** Display name for the UI (singular form) */
|
|
1141
|
-
label: LocalizedString;
|
|
1142
|
-
/** Display name for the UI (plural form) */
|
|
1143
|
-
labelPlural?: LocalizedString;
|
|
1144
|
-
/** Field used as display label (optional for computed entities) */
|
|
1145
|
-
labelField?: string;
|
|
1146
|
-
/** Field definitions for the computed result structure */
|
|
1147
|
-
fields: Record<string, FieldDefinition>;
|
|
1148
|
-
/** Function that computes and returns the data. Receives query params from URL */
|
|
1149
|
-
compute?: (ctx: ModuleContext, params?: Record<string, unknown>) => Promise<unknown[]>;
|
|
1150
|
-
/** Caching to avoid recomputation on every request */
|
|
1151
|
-
cache?: {
|
|
1152
|
-
/** Cache time-to-live in seconds. 0 = no caching */
|
|
1153
|
-
ttl: number;
|
|
1154
|
-
/** Event patterns that trigger cache invalidation */
|
|
1155
|
-
invalidateOn?: string[];
|
|
1156
|
-
};
|
|
1157
|
-
/** If true, returns single object instead of array (for dashboards, system info) */
|
|
1158
|
-
isSingle?: boolean;
|
|
1159
|
-
/** CASL authorization for read operations */
|
|
1160
|
-
casl?: EntityCaslConfig;
|
|
1161
|
-
/** API route prefix */
|
|
1162
|
-
routePrefix?: string;
|
|
1163
|
-
/** UI sidebar ordering (default: 999) */
|
|
1164
|
-
order?: number;
|
|
1165
|
-
/** UI display mode: 'table', 'list', or 'masonry' */
|
|
1166
|
-
displayMode?: DisplayMode;
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* View entity - read-only database view or projection.
|
|
1170
|
-
*
|
|
1171
|
-
* Use for optimized read patterns, denormalized data, or SQL VIEWs.
|
|
1172
|
-
* Can reference actual database views or define virtual queries.
|
|
1173
|
-
*
|
|
1174
|
-
* @example
|
|
1175
|
-
* ```typescript
|
|
1176
|
-
* const orderSummary: ViewEntityDefinition = {
|
|
1177
|
-
* type: 'view',
|
|
1178
|
-
* table: 'v_order_summary', // actual SQL VIEW
|
|
1179
|
-
* label: { en: 'Order Summary', es: 'Resumen de Pedidos' },
|
|
1180
|
-
* labelField: 'order_number',
|
|
1181
|
-
* sourceEntity: 'orders',
|
|
1182
|
-
* query: (db) => db('orders')
|
|
1183
|
-
* .select('orders.*', 'customers.name as customer_name')
|
|
1184
|
-
* .join('customers', 'orders.customer_id', 'customers.id'),
|
|
1185
|
-
* fields: { ... }
|
|
1186
|
-
* }
|
|
1187
|
-
* ```
|
|
1188
|
-
*/
|
|
1189
|
-
interface ViewEntityDefinition {
|
|
1190
|
-
/** Entity type identifier */
|
|
1191
|
-
type: 'view';
|
|
1192
|
-
/** Database table or SQL VIEW name */
|
|
1193
|
-
table: string;
|
|
1194
|
-
/** Display name for the UI (singular form) */
|
|
1195
|
-
label: LocalizedString;
|
|
1196
|
-
/** Display name for the UI (plural form) */
|
|
1197
|
-
labelPlural?: LocalizedString;
|
|
1198
|
-
/** Field used as display label in lists and references */
|
|
1199
|
-
labelField: string;
|
|
1200
|
-
/** Field definitions for the view columns */
|
|
1201
|
-
fields: Record<string, FieldDefinition>;
|
|
1202
|
-
/** Source entity this view derives from (for documentation) */
|
|
1203
|
-
sourceEntity?: string;
|
|
1204
|
-
/** SQL query string or Knex query builder function to define the view */
|
|
1205
|
-
query?: string | ((db: Knex) => Knex.QueryBuilder);
|
|
1206
|
-
/** CASL authorization for read operations */
|
|
1207
|
-
casl?: EntityCaslConfig;
|
|
1208
|
-
/** API route prefix */
|
|
1209
|
-
routePrefix?: string;
|
|
1210
|
-
/** UI sidebar ordering (default: 999) */
|
|
1211
|
-
order?: number;
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Config entity - scoped configuration settings.
|
|
1215
|
-
*
|
|
1216
|
-
* Similar to single entity but with scoping support for multi-tenant,
|
|
1217
|
-
* per-module, or per-user configurations.
|
|
1218
|
-
*
|
|
1219
|
-
* @example
|
|
1220
|
-
* ```typescript
|
|
1221
|
-
* const moduleConfig: ConfigEntityDefinition = {
|
|
1222
|
-
* type: 'config',
|
|
1223
|
-
* table: 'module_configs',
|
|
1224
|
-
* label: { en: 'Module Config', es: 'Config de Módulo' },
|
|
1225
|
-
* scope: 'module',
|
|
1226
|
-
* scopeField: 'module_name',
|
|
1227
|
-
* defaults: { enableFeatureX: false, maxItems: 100 },
|
|
1228
|
-
* timestamps: true,
|
|
1229
|
-
* fields: {
|
|
1230
|
-
* module_name: { name: 'module_name', label: 'Module', input: 'text' },
|
|
1231
|
-
* enableFeatureX: { name: 'enableFeatureX', label: 'Feature X', input: 'switch' },
|
|
1232
|
-
* maxItems: { name: 'maxItems', label: 'Max Items', input: 'number' }
|
|
1233
|
-
* }
|
|
1234
|
-
* }
|
|
1235
|
-
* ```
|
|
1236
|
-
*/
|
|
1237
|
-
interface ConfigEntityDefinition extends BaseEntityDefinition {
|
|
1238
|
-
/** Entity type identifier */
|
|
1239
|
-
type: 'config';
|
|
1240
|
-
/** Field that identifies the configuration scope (e.g., 'module_name', 'tenant_id', 'user_id') */
|
|
1241
|
-
scopeField?: string;
|
|
1242
|
-
/** Configuration scope level: 'global', 'module', 'tenant', or 'user' */
|
|
1243
|
-
scope?: 'global' | 'module' | 'tenant' | 'user';
|
|
1244
|
-
/** Default values applied when config is first accessed */
|
|
1245
|
-
defaults?: Record<string, unknown>;
|
|
1246
|
-
/** Auto-add updated_at timestamp column */
|
|
1247
|
-
timestamps?: boolean;
|
|
1248
|
-
/** Auto-add updated_by user reference column */
|
|
1249
|
-
audit?: boolean;
|
|
1250
|
-
}
|
|
1251
|
-
/**
|
|
1252
|
-
* Temporary entity - short-lived data with automatic expiration.
|
|
1253
|
-
*
|
|
1254
|
-
* Use for OTP codes, session tokens, password reset links, cache entries.
|
|
1255
|
-
* Records are automatically deleted after TTL expires.
|
|
1256
|
-
*
|
|
1257
|
-
* @example
|
|
1258
|
-
* ```typescript
|
|
1259
|
-
* const otpCodes: TempEntityDefinition = {
|
|
1260
|
-
* type: 'temp',
|
|
1261
|
-
* table: 'auth_otp_codes',
|
|
1262
|
-
* label: { en: 'OTP Code', es: 'Código OTP' },
|
|
1263
|
-
* ttl: 300, // 5 minutes
|
|
1264
|
-
* indexes: [{ columns: ['user_id', 'code'] }],
|
|
1265
|
-
* fields: {
|
|
1266
|
-
* user_id: { name: 'user_id', label: 'User', input: 'select', ... },
|
|
1267
|
-
* code: { name: 'code', label: 'Code', input: 'text', ... },
|
|
1268
|
-
* expires_at: { name: 'expires_at', label: 'Expires', input: 'datetime', ... }
|
|
1269
|
-
* }
|
|
1270
|
-
* }
|
|
1271
|
-
* ```
|
|
1272
|
-
*/
|
|
1273
|
-
interface TempEntityDefinition extends BaseEntityDefinition {
|
|
1274
|
-
/** Entity type identifier */
|
|
1275
|
-
type: 'temp';
|
|
1276
|
-
/** Time-to-live in seconds. Records are deleted after this duration */
|
|
1277
|
-
ttl: number;
|
|
1278
|
-
/** Field that stores the expiration timestamp (default: 'expires_at') */
|
|
1279
|
-
ttlField?: string;
|
|
1280
|
-
/** Field used as display label in lists (optional for temp entities) */
|
|
1281
|
-
labelField?: string;
|
|
1282
|
-
/** Database indexes for efficient lookups */
|
|
1283
|
-
indexes?: EntityIndex[];
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* All available entity type identifiers.
|
|
1287
|
-
*
|
|
1288
|
-
* @see {@link EntityDefinition} for the full discriminated union with documentation
|
|
1289
|
-
*/
|
|
1290
|
-
type EntityType = 'collection' | 'single' | 'external' | 'virtual' | 'computed' | 'view' | 'reference' | 'config' | 'event' | 'temp' | 'action';
|
|
1291
|
-
/**
|
|
1292
|
-
* Display mode for entity list views in the UI.
|
|
1293
|
-
*
|
|
1294
|
-
* - `table` - Data grid with columns, sorting, filtering (default)
|
|
1295
|
-
* - `list` - Card-based vertical list
|
|
1296
|
-
* - `masonry` - Pinterest-style image gallery grid
|
|
1297
|
-
*/
|
|
1298
|
-
type DisplayMode = 'table' | 'list' | 'masonry';
|
|
1299
|
-
/**
|
|
1300
|
-
* Union of all entity definitions
|
|
1301
|
-
*
|
|
1302
|
-
* | Type | Persistence | CRUD | Primary use |
|
|
1303
|
-
* |------------|-------------|-----------------|----------------------------------|
|
|
1304
|
-
* | collection | Yes (DB) | Full | Business data (users, posts) |
|
|
1305
|
-
* | single | Yes (DB) | Update/Read | Global config (site_config) |
|
|
1306
|
-
* | external | No | Read | External data (stripe_customers) |
|
|
1307
|
-
* | virtual | No | Read | Orchestration (unified_customers)|
|
|
1308
|
-
* | computed | No/optional | Read | KPIs, stats |
|
|
1309
|
-
* | view | Yes/virtual | Read | Optimized reads (projections) |
|
|
1310
|
-
* | reference | Yes | Read (admin) | Catalogs (countries, currencies) |
|
|
1311
|
-
* | config | Yes | Update/Read | Per-module config |
|
|
1312
|
-
* | event | Yes | Append | Audit logs (audit_logs) |
|
|
1313
|
-
* | temp | No (TTL) | Read/Write | Cache, sessions (otp_codes) |
|
|
1314
|
-
* | action | No | Execute | Operations, workflows |
|
|
1315
|
-
*/
|
|
1316
|
-
type EntityDefinition = CollectionEntityDefinition | SingleEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition | ViewEntityDefinition | ReferenceEntityDefinition | ConfigEntityDefinition | EventEntityDefinition | TempEntityDefinition | ActionEntityDefinition;
|
|
1317
|
-
/**
|
|
1318
|
-
* Requirements that must be met to activate a module.
|
|
1319
|
-
*
|
|
1320
|
-
* Modules with unmet requirements are skipped during initialization.
|
|
1321
|
-
*
|
|
1322
|
-
* @example
|
|
1323
|
-
* ```typescript
|
|
1324
|
-
* requirements: {
|
|
1325
|
-
* env: ['STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET'],
|
|
1326
|
-
* modules: ['auth', 'storage']
|
|
1327
|
-
* }
|
|
1328
|
-
* ```
|
|
1329
|
-
*/
|
|
1330
|
-
interface ModuleRequirements {
|
|
1331
|
-
/** Environment variables that must be defined */
|
|
1332
|
-
env?: string[];
|
|
1333
|
-
/** Other modules that must be loaded first */
|
|
1334
|
-
modules?: string[];
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Generic interface for CASL ability.
|
|
1338
|
-
*
|
|
1339
|
-
* Provides permission checking without directly depending on @casl/ability package.
|
|
1340
|
-
* Used in request objects for authorization checks.
|
|
1341
|
-
*
|
|
1342
|
-
* @example
|
|
1343
|
-
* ```typescript
|
|
1344
|
-
* if (req.ability.can('update', post)) {
|
|
1345
|
-
* // User can update this post
|
|
1346
|
-
* }
|
|
1347
|
-
* if (req.ability.cannot('delete', post)) {
|
|
1348
|
-
* throw new ForbiddenError('Cannot delete post')
|
|
1349
|
-
* }
|
|
1350
|
-
* ```
|
|
1351
|
-
*/
|
|
1352
|
-
interface AbilityLike {
|
|
1353
|
-
/** Check if action is allowed on subject */
|
|
1354
|
-
can: (action: string, subject: unknown) => boolean;
|
|
1355
|
-
/** Check if action is denied on subject */
|
|
1356
|
-
cannot: (action: string, subject: unknown) => boolean;
|
|
1357
|
-
}
|
|
1358
|
-
/**
|
|
1359
|
-
* CASL ForbiddenError instance for throwing permission errors.
|
|
1360
|
-
*/
|
|
1361
|
-
interface ForbiddenErrorInstance {
|
|
1362
|
-
/** Throws ForbiddenError if action is not allowed on subject */
|
|
1363
|
-
throwUnlessCan: (action: string, subject: unknown) => void;
|
|
1364
|
-
}
|
|
1365
|
-
/**
|
|
1366
|
-
* CASL ForbiddenError constructor for creating error instances.
|
|
1367
|
-
*/
|
|
1368
|
-
interface ForbiddenErrorConstructor {
|
|
1369
|
-
/** Creates a ForbiddenErrorInstance bound to an ability */
|
|
1370
|
-
from: (ability: any) => ForbiddenErrorInstance;
|
|
1371
|
-
}
|
|
1372
|
-
/**
|
|
1373
|
-
* CASL abilities API available in module context.
|
|
1374
|
-
*
|
|
1375
|
-
* Allows modules to use CASL for authorization without importing @casl/ability directly.
|
|
1376
|
-
*
|
|
1377
|
-
* @example
|
|
1378
|
-
* ```typescript
|
|
1379
|
-
* // In module handler
|
|
1380
|
-
* const ability = ctx.abilities.defineAbilityFor(user)
|
|
1381
|
-
* ctx.abilities.ForbiddenError.from(ability).throwUnlessCan('update', post)
|
|
1382
|
-
* ```
|
|
1383
|
-
*/
|
|
1384
|
-
interface ModuleAbilities {
|
|
1385
|
-
/** Creates CASL ability instance for a user based on their roles */
|
|
1386
|
-
defineAbilityFor: (user: {
|
|
1387
|
-
id: string;
|
|
1388
|
-
} | null, ...args: unknown[]) => unknown;
|
|
1389
|
-
/** Serializes CASL rules for client-side authorization (frontend) */
|
|
1390
|
-
packRules: (ability: unknown) => unknown[];
|
|
1391
|
-
/** Wraps an object as CASL subject for instance-level permission checks */
|
|
1392
|
-
subject: (type: string, object: Record<string, unknown>) => unknown;
|
|
1393
|
-
/** ForbiddenError constructor for throwUnlessCan pattern */
|
|
1394
|
-
ForbiddenError: ForbiddenErrorConstructor;
|
|
1395
|
-
}
|
|
1396
|
-
/**
|
|
1397
|
-
* Pre-typed authenticated request with BaseUser and AbilityLike.
|
|
1398
|
-
*
|
|
1399
|
-
* Use this type for route handlers that require authentication.
|
|
1400
|
-
*
|
|
1401
|
-
* @example
|
|
1402
|
-
* ```typescript
|
|
1403
|
-
* const handler = async (req: AuthRequest, res: Response) => {
|
|
1404
|
-
* const userId = req.user.id
|
|
1405
|
-
* if (req.ability.can('read', 'Post')) { ... }
|
|
1406
|
-
* }
|
|
1407
|
-
* ```
|
|
1408
|
-
*/
|
|
1409
|
-
type AuthRequest = BaseAuthRequest<BaseUser, AbilityLike>;
|
|
1410
|
-
/**
|
|
1411
|
-
* Helper utilities available in module context.
|
|
1412
|
-
*
|
|
1413
|
-
* Provides common operations for migrations, ID generation, and path resolution.
|
|
1414
|
-
*/
|
|
1415
|
-
interface ContextHelpers {
|
|
1416
|
-
/** Generates a unique UUID v4 identifier */
|
|
1417
|
-
generateId: () => string;
|
|
1418
|
-
/** Adds created_at and updated_at columns to a table during migration */
|
|
1419
|
-
addTimestamps: (table: Knex.CreateTableBuilder, db: Knex) => void;
|
|
1420
|
-
/** Adds created_by and updated_by columns if they don't exist */
|
|
1421
|
-
addAuditFieldsIfMissing: (db: Knex, tableName: string) => Promise<void>;
|
|
1422
|
-
/** Adds is_default boolean field for config tables */
|
|
1423
|
-
addConfigDefaultField: (db: Knex, tableName: string) => Promise<void>;
|
|
1424
|
-
/** Adds a column only if it doesn't exist. Returns true if column was added */
|
|
1425
|
-
addColumnIfMissing: (db: Knex, tableName: string, columnName: string, columnBuilder: (table: Knex.AlterTableBuilder) => void) => Promise<boolean>;
|
|
1426
|
-
/** Gets the path to the nexus-backend library directory */
|
|
1427
|
-
getLibPath: () => string;
|
|
1428
|
-
/** Gets the path to the user's project root directory */
|
|
1429
|
-
getProjectPath: () => string;
|
|
1430
|
-
/** Returns current timestamp formatted for the DB driver (MySQL: 'YYYY-MM-DD HH:MM:SS', others: ISO 8601) */
|
|
1431
|
-
nowTimestamp: (db: Knex) => string;
|
|
1432
|
-
/** Formats a Date object for the current DB driver */
|
|
1433
|
-
formatTimestamp: (db: Knex, date?: Date) => string;
|
|
1434
|
-
/**
|
|
1435
|
-
* Safely parses JSON with automatic error logging.
|
|
1436
|
-
* Returns fallback value if parsing fails instead of throwing.
|
|
1437
|
-
* @param jsonString - JSON string to parse
|
|
1438
|
-
* @param fallback - Value to return if parsing fails
|
|
1439
|
-
* @param context - Optional context for logging (e.g. { notificationId: 'x', context: 'markAsRead' })
|
|
1440
|
-
*/
|
|
1441
|
-
safeJsonParse: <T>(jsonString: string, fallback: T, context?: Record<string, unknown>) => T;
|
|
1442
|
-
}
|
|
1443
|
-
/**
|
|
1444
|
-
* Cryptographic utilities for password handling.
|
|
1445
|
-
*
|
|
1446
|
-
* Uses bcrypt with cost factor 12 for secure password hashing.
|
|
1447
|
-
*/
|
|
1448
|
-
interface ContextCrypto {
|
|
1449
|
-
/** Hashes a password using bcrypt (cost factor 12) */
|
|
1450
|
-
hashPassword: (password: string) => Promise<string>;
|
|
1451
|
-
/** Verifies password against bcrypt hash (timing-safe comparison) */
|
|
1452
|
-
verifyPassword: (password: string, hash: string) => Promise<boolean>;
|
|
1453
|
-
/** Pre-computed dummy hash for timing-safe comparison when user doesn't exist */
|
|
1454
|
-
DUMMY_HASH: string;
|
|
1455
|
-
}
|
|
1456
|
-
/**
|
|
1457
|
-
* Socket.IO real-time communication API.
|
|
1458
|
-
*
|
|
1459
|
-
* Provides access to WebSocket connections for real-time features.
|
|
1460
|
-
* Only available if Socket.IO is initialized in the backend configuration.
|
|
1461
|
-
*/
|
|
1462
|
-
/**
|
|
1463
|
-
* Socket.IO Server interface (minimal type-safe subset)
|
|
1464
|
-
* Avoids importing socket.io package as dependency in SDK
|
|
1465
|
-
*/
|
|
1466
|
-
interface SocketIOServer {
|
|
1467
|
-
/** Emits event to a specific room */
|
|
1468
|
-
to(room: string): SocketIOBroadcastOperator;
|
|
1469
|
-
/** Emits event to a specific room (alias for to) */
|
|
1470
|
-
in(room: string): SocketIOBroadcastOperator;
|
|
1471
|
-
/** Emits event to all connected clients */
|
|
1472
|
-
emit(event: string, ...args: unknown[]): boolean;
|
|
1473
|
-
/** Access to all connected sockets */
|
|
1474
|
-
sockets: {
|
|
1475
|
-
sockets: Map<string, unknown>;
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
/**
|
|
1479
|
-
* Socket.IO Broadcast Operator interface (minimal type-safe subset)
|
|
1480
|
-
*/
|
|
1481
|
-
interface SocketIOBroadcastOperator {
|
|
1482
|
-
/** Emits event to all sockets in the room */
|
|
1483
|
-
emit(event: string, ...args: unknown[]): void;
|
|
1484
|
-
/** Fetches all sockets in the room */
|
|
1485
|
-
fetchSockets(): Promise<unknown[]>;
|
|
1486
|
-
}
|
|
1487
|
-
interface ContextSocket {
|
|
1488
|
-
/** Gets the Socket.IO server instance. Throws if not initialized */
|
|
1489
|
-
getIO: () => SocketIOServer;
|
|
1490
|
-
/** Checks if Socket.IO server is running */
|
|
1491
|
-
isInitialized: () => boolean;
|
|
1492
|
-
/** Checks if a specific user has active WebSocket connections */
|
|
1493
|
-
isUserConnected: (userId: string) => boolean;
|
|
1494
|
-
/** Gets the number of active sockets for a user (multiple tabs/devices) */
|
|
1495
|
-
getUserSocketCount: (userId: string) => number;
|
|
1496
|
-
/** Gets array of all connected user IDs */
|
|
1497
|
-
getConnectedUsers: () => string[];
|
|
1498
|
-
/** Joins a user to a custom room (all their sockets) */
|
|
1499
|
-
joinUserToRoom: (userId: string, room: string) => boolean;
|
|
1500
|
-
/** Removes a user from a custom room (all their sockets) */
|
|
1501
|
-
removeUserFromRoom: (userId: string, room: string) => boolean;
|
|
1502
|
-
/** Joins a specific socket to a room */
|
|
1503
|
-
joinSocketToRoom: (socketId: string, room: string) => boolean;
|
|
1504
|
-
/** Removes a specific socket from a room */
|
|
1505
|
-
removeSocketFromRoom: (socketId: string, room: string) => boolean;
|
|
1506
|
-
/** Gets all user IDs in a room */
|
|
1507
|
-
getRoomMembers: (room: string) => Promise<string[]>;
|
|
1508
|
-
/** Gets the number of sockets in a room */
|
|
1509
|
-
getRoomSize: (room: string) => Promise<number>;
|
|
1510
|
-
/** Gets all custom rooms a user is in (excluding system rooms) */
|
|
1511
|
-
getUserRooms: (userId: string) => string[];
|
|
1512
|
-
/** Checks if a room has any members */
|
|
1513
|
-
roomExists: (room: string) => Promise<boolean>;
|
|
1514
|
-
/** Emits an event to a specific room */
|
|
1515
|
-
emitToRoom: (room: string, event: string, data: unknown) => boolean;
|
|
1516
|
-
/** Emits an event to a specific user (all their sockets) */
|
|
1517
|
-
emitToUser: (userId: string, event: string, data: unknown) => boolean;
|
|
1518
|
-
/** Emits an event to all users with a specific role */
|
|
1519
|
-
emitToRole: (roleId: string, event: string, data: unknown) => boolean;
|
|
1520
|
-
/** Emits an event to all connected sockets */
|
|
1521
|
-
emitToAll: (event: string, data: unknown) => boolean;
|
|
1522
|
-
/** Emits an event to all authenticated users */
|
|
1523
|
-
emitToAuthenticated: (event: string, data: unknown) => boolean;
|
|
1524
|
-
/** Broadcasts to a room except specific user(s) */
|
|
1525
|
-
broadcastToRoom: (room: string, exceptUserIds: string | string[], event: string, data: unknown) => boolean;
|
|
1526
|
-
}
|
|
1527
|
-
/**
|
|
1528
|
-
* Application manifest with module and plugin registry.
|
|
1529
|
-
*
|
|
1530
|
-
* Represents either the core nexus-backend manifest or a user project manifest.
|
|
1531
|
-
*/
|
|
1532
|
-
interface AppManifest {
|
|
1533
|
-
/** Package name from package.json */
|
|
1534
|
-
name: string;
|
|
1535
|
-
/** Package version from package.json */
|
|
1536
|
-
version: string;
|
|
1537
|
-
/** Registered modules in this application */
|
|
1538
|
-
modules: ModuleManifest[];
|
|
1539
|
-
/** Registered plugins (only present in user manifest) */
|
|
1540
|
-
plugins?: PluginManifest[];
|
|
1541
|
-
}
|
|
1542
|
-
/**
|
|
1543
|
-
* Engine API for runtime introspection of modules and plugins.
|
|
1544
|
-
*
|
|
1545
|
-
* Provides access to the complete registry of loaded components.
|
|
1546
|
-
*/
|
|
1547
|
-
interface ContextEngine {
|
|
1548
|
-
/** Gets all registered modules (core + user) */
|
|
1549
|
-
getModules: () => ModuleManifest[];
|
|
1550
|
-
/** Gets all registered plugins */
|
|
1551
|
-
getPlugins: () => PluginManifest[];
|
|
1552
|
-
/** Gets CASL subjects defined by a specific module */
|
|
1553
|
-
getModuleSubjects: (mod: ModuleManifest) => string[];
|
|
1554
|
-
/** Gets all CASL subjects registered across all modules */
|
|
1555
|
-
getRegisteredSubjects: () => string[];
|
|
1556
|
-
/** Gets modules from nexus-backend core (auth, users, storage, etc.) */
|
|
1557
|
-
getCoreModules: () => ModuleManifest[];
|
|
1558
|
-
/** Gets modules from user project and plugins */
|
|
1559
|
-
getUserModules: () => ModuleManifest[];
|
|
1560
|
-
/** Gets the nexus-backend library manifest */
|
|
1561
|
-
getCoreManifest: () => AppManifest;
|
|
1562
|
-
/** Gets the user project manifest. Returns null in standalone development mode */
|
|
1563
|
-
getUserManifest: () => AppManifest | null;
|
|
1564
|
-
/** Returns true if running as a library inside a user project */
|
|
1565
|
-
hasUserApp: () => boolean;
|
|
1566
|
-
}
|
|
1567
|
-
/**
|
|
1568
|
-
* Generic validation schema interface compatible with Zod.
|
|
1569
|
-
*
|
|
1570
|
-
* Any object with a parse() method that throws on invalid data works.
|
|
1571
|
-
*/
|
|
1572
|
-
interface ValidationSchema {
|
|
1573
|
-
/** Parses and validates data. Throws on validation failure */
|
|
1574
|
-
parse: (data: unknown) => unknown;
|
|
1575
|
-
}
|
|
1576
|
-
/**
|
|
1577
|
-
* Schemas for the validation middleware.
|
|
1578
|
-
*
|
|
1579
|
-
* Validates request body, query parameters, and URL parameters.
|
|
1580
|
-
*/
|
|
1581
|
-
interface ValidateSchemas {
|
|
1582
|
-
/** Schema for request body (req.body) */
|
|
1583
|
-
body?: ValidationSchema;
|
|
1584
|
-
/** Schema for query parameters (req.query) */
|
|
1585
|
-
query?: ValidationSchema;
|
|
1586
|
-
/** Schema for URL parameters (req.params) */
|
|
1587
|
-
params?: ValidationSchema;
|
|
1588
|
-
}
|
|
1589
|
-
/**
|
|
1590
|
-
* Configuration options for rate limiting middleware.
|
|
1591
|
-
*/
|
|
1592
|
-
interface RateLimitOptions {
|
|
1593
|
-
/** Time window duration in milliseconds (default: 60000 = 1 minute) */
|
|
1594
|
-
windowMs?: number;
|
|
1595
|
-
/** Maximum requests allowed per window (default: 100) */
|
|
1596
|
-
max?: number;
|
|
1597
|
-
/** Error message when rate limit is exceeded */
|
|
1598
|
-
message?: string;
|
|
1599
|
-
}
|
|
1600
|
-
/**
|
|
1601
|
-
* Middleware factories available in module context.
|
|
1602
|
-
*
|
|
1603
|
-
* Provides common Express middlewares for validation, rate limiting, etc.
|
|
1604
|
-
*/
|
|
1605
|
-
interface ModuleMiddlewares {
|
|
1606
|
-
/** Creates validation middleware for body/query/params */
|
|
1607
|
-
validate: (schemas: ValidateSchemas) => RequestHandler;
|
|
1608
|
-
/** Creates rate limiting middleware */
|
|
1609
|
-
rateLimit: (options?: RateLimitOptions) => RequestHandler;
|
|
1610
|
-
/** Extensible: additional middlewares can be registered */
|
|
1611
|
-
[key: string]: RequestHandler | ((...args: any[]) => RequestHandler) | undefined;
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Minimal EventEmitter interface compatible with EventEmitter2.
|
|
1615
|
-
*
|
|
1616
|
-
* Used for inter-module communication and event-driven architecture.
|
|
1617
|
-
*/
|
|
1618
|
-
interface EventEmitterLike {
|
|
1619
|
-
/** Emits an event with arguments. Returns true if listeners exist */
|
|
1620
|
-
emit: (event: string, ...args: unknown[]) => boolean;
|
|
1621
|
-
/** Registers an event listener */
|
|
1622
|
-
on: (event: string, listener: (...args: unknown[]) => void) => this;
|
|
1623
|
-
/** Removes an event listener */
|
|
1624
|
-
off: (event: string, listener: (...args: unknown[]) => void) => this;
|
|
1625
|
-
/** Registers a one-time event listener */
|
|
1626
|
-
once: (event: string, listener: (...args: unknown[]) => void) => this;
|
|
1627
|
-
/** Registers a listener for all events (EventEmitter2 feature) */
|
|
1628
|
-
onAny?: (listener: (event: string, ...args: unknown[]) => void) => this;
|
|
1629
|
-
}
|
|
1630
|
-
/**
|
|
1631
|
-
* Error reporter interface for external monitoring services.
|
|
1632
|
-
*
|
|
1633
|
-
* Compatible with Sentry, GlitchTip, or any error tracking service.
|
|
1634
|
-
*/
|
|
1635
|
-
interface LoggerReporter {
|
|
1636
|
-
/** Captures and reports an exception to the monitoring service */
|
|
1637
|
-
captureException: (error: Error, context?: Record<string, unknown>) => void;
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Filter operators for advanced filtering
|
|
1641
|
-
*
|
|
1642
|
-
* @example
|
|
1643
|
-
* // Simple equality (shorthand)
|
|
1644
|
-
* { status: 'active' }
|
|
1645
|
-
*
|
|
1646
|
-
* // With operators
|
|
1647
|
-
* { name: { $contains: 'john' } }
|
|
1648
|
-
* { age: { $gte: 18 } }
|
|
1649
|
-
* { email: { $startswith: 'admin' } }
|
|
1650
|
-
*
|
|
1651
|
-
* // Array (IN operator shorthand)
|
|
1652
|
-
* { status: ['active', 'pending'] }
|
|
1653
|
-
*/
|
|
1654
|
-
interface FilterOperators {
|
|
1655
|
-
/** Equal (default if no operator) */
|
|
1656
|
-
$eq?: unknown;
|
|
1657
|
-
/** Not equal */
|
|
1658
|
-
$ne?: unknown;
|
|
1659
|
-
/** Greater than */
|
|
1660
|
-
$gt?: number | string | Date;
|
|
1661
|
-
/** Greater than or equal */
|
|
1662
|
-
$gte?: number | string | Date;
|
|
1663
|
-
/** Less than */
|
|
1664
|
-
$lt?: number | string | Date;
|
|
1665
|
-
/** Less than or equal */
|
|
1666
|
-
$lte?: number | string | Date;
|
|
1667
|
-
/** Contains (LIKE %value%) */
|
|
1668
|
-
$contains?: string;
|
|
1669
|
-
/** Starts with (LIKE value%) */
|
|
1670
|
-
$startswith?: string;
|
|
1671
|
-
/** Ends with (LIKE %value) */
|
|
1672
|
-
$endswith?: string;
|
|
1673
|
-
/** In array */
|
|
1674
|
-
$in?: unknown[];
|
|
1675
|
-
/** Not in array */
|
|
1676
|
-
$nin?: unknown[];
|
|
1677
|
-
/** Is null (true = IS NULL, false = IS NOT NULL) */
|
|
1678
|
-
$isnull?: boolean;
|
|
1679
|
-
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Filter value can be:
|
|
1682
|
-
* - Primitive (shorthand for $eq)
|
|
1683
|
-
* - Array (shorthand for $in)
|
|
1684
|
-
* - Object with operators
|
|
1685
|
-
*/
|
|
1686
|
-
type FilterValue = string | number | boolean | null | unknown[] | FilterOperators;
|
|
1687
|
-
/**
|
|
1688
|
-
* Query parameters for entity list operations
|
|
1689
|
-
*
|
|
1690
|
-
* @example
|
|
1691
|
-
* // Simple filters
|
|
1692
|
-
* { filters: { status: 'active' } }
|
|
1693
|
-
*
|
|
1694
|
-
* // With operators (see FilterOperators)
|
|
1695
|
-
* { filters: { name: { $contains: 'john' }, age: { $gte: 18 } } }
|
|
1696
|
-
*/
|
|
1697
|
-
interface EntityQuery {
|
|
1698
|
-
page?: number;
|
|
1699
|
-
limit?: number;
|
|
1700
|
-
sort?: string;
|
|
1701
|
-
order?: 'asc' | 'desc';
|
|
1702
|
-
search?: string;
|
|
1703
|
-
/** Filters with optional operators - use FilterValue type for type safety */
|
|
1704
|
-
filters?: Record<string, unknown>;
|
|
1705
|
-
}
|
|
1706
|
-
/**
|
|
1707
|
-
* Role management service interface.
|
|
1708
|
-
*
|
|
1709
|
-
* Provides CRUD operations for roles. Used internally by UsersService.
|
|
1710
|
-
*/
|
|
1711
|
-
interface BaseRolesService {
|
|
1712
|
-
/** Gets paginated list of all roles */
|
|
1713
|
-
findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
|
|
1714
|
-
/** Gets a role by its ID */
|
|
1715
|
-
findById(id: string): Promise<unknown>;
|
|
1716
|
-
/** Gets a role by its name (e.g., 'ADMIN', 'EDITOR') */
|
|
1717
|
-
findByName(name: string): Promise<unknown | undefined>;
|
|
1718
|
-
/** Creates a new role */
|
|
1719
|
-
create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
|
|
1720
|
-
/** Updates an existing role */
|
|
1721
|
-
update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
|
|
1722
|
-
/** Deletes a role by ID */
|
|
1723
|
-
delete(id: string): Promise<void>;
|
|
1724
|
-
}
|
|
1725
|
-
/**
|
|
1726
|
-
* User management service interface.
|
|
1727
|
-
*
|
|
1728
|
-
* Provides user CRUD and role assignment operations.
|
|
1729
|
-
* The backend extends this with concrete types (UserWithRole, etc.).
|
|
1730
|
-
*/
|
|
1731
|
-
interface BaseUsersService {
|
|
1732
|
-
/** Gets paginated list of users */
|
|
1733
|
-
findAll(query?: EntityQuery): Promise<PaginatedResult<unknown>>;
|
|
1734
|
-
/** Gets a user by ID without role information */
|
|
1735
|
-
findById(id: string): Promise<unknown>;
|
|
1736
|
-
/** Gets a user by ID including their assigned roles */
|
|
1737
|
-
findByIdWithRoles(id: string): Promise<unknown | null>;
|
|
1738
|
-
/** Creates a new user */
|
|
1739
|
-
create(data: Record<string, unknown>, userId?: string): Promise<unknown>;
|
|
1740
|
-
/** Updates an existing user */
|
|
1741
|
-
update(id: string, data: Record<string, unknown>, userId?: string): Promise<unknown>;
|
|
1742
|
-
/** Deletes a user by ID */
|
|
1743
|
-
delete(id: string): Promise<void>;
|
|
1744
|
-
/** Gets array of role IDs for a user */
|
|
1745
|
-
getRoleIds(userId: string): Promise<string[]>;
|
|
1746
|
-
/** Gets array of role names for a user (e.g., ['ADMIN', 'EDITOR']) */
|
|
1747
|
-
getRoleNames(userId: string): Promise<string[]>;
|
|
1748
|
-
/** Gets full role objects for a user */
|
|
1749
|
-
getUserRoles(userId: string): Promise<unknown[]>;
|
|
1750
|
-
/** Assigns a single role to a user (adds to existing roles) */
|
|
1751
|
-
assignRole(userId: string, roleId: string): Promise<void>;
|
|
1752
|
-
/** Removes a single role from a user */
|
|
1753
|
-
removeRole(userId: string, roleId: string): Promise<void>;
|
|
1754
|
-
/** Replaces all user roles with the provided list */
|
|
1755
|
-
setRoles(userId: string, roleIds: string[]): Promise<void>;
|
|
1756
|
-
/** Nested roles service for direct role management */
|
|
1757
|
-
roles: BaseRolesService;
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Email sending options.
|
|
1761
|
-
*
|
|
1762
|
-
* Supports both simple text emails and rich HTML templates with actions.
|
|
1763
|
-
*/
|
|
1764
|
-
interface SendMailOptions {
|
|
1765
|
-
/** Recipient email address(es) */
|
|
1766
|
-
to: string | string[];
|
|
1767
|
-
/** Email subject line */
|
|
1768
|
-
subject: string;
|
|
1769
|
-
/** Title for email template (displayed prominently) */
|
|
1770
|
-
title?: string;
|
|
1771
|
-
/** Plain text message for simple emails */
|
|
1772
|
-
message?: string;
|
|
1773
|
-
/** Full HTML content (overrides template) */
|
|
1774
|
-
html?: string;
|
|
1775
|
-
/** Plain text fallback (generated from HTML if not provided) */
|
|
1776
|
-
text?: string;
|
|
1777
|
-
/** Sender email address (uses default if not provided) */
|
|
1778
|
-
from?: string;
|
|
1779
|
-
/** Reply-to email address */
|
|
1780
|
-
replyTo?: string;
|
|
1781
|
-
/** CTA buttons for email template */
|
|
1782
|
-
actions?: Array<{
|
|
1783
|
-
label: string;
|
|
1784
|
-
url: string;
|
|
1785
|
-
}>;
|
|
1786
|
-
/** Logo URL for email header */
|
|
1787
|
-
logoUrl?: string;
|
|
1788
|
-
/** File attachments */
|
|
1789
|
-
attachments?: Array<{
|
|
1790
|
-
filename: string;
|
|
1791
|
-
content: Buffer | string;
|
|
1792
|
-
contentType?: string;
|
|
1793
|
-
}>;
|
|
1794
|
-
}
|
|
1795
|
-
/**
|
|
1796
|
-
* Result from sending an email.
|
|
1797
|
-
*/
|
|
1798
|
-
interface SendMailResult {
|
|
1799
|
-
/** Unique message ID from the mail server */
|
|
1800
|
-
messageId: string;
|
|
1801
|
-
/** Email addresses that accepted the message */
|
|
1802
|
-
accepted: string[];
|
|
1803
|
-
/** Email addresses that rejected the message */
|
|
1804
|
-
rejected: string[];
|
|
1805
|
-
}
|
|
1806
|
-
/**
|
|
1807
|
-
* Email service interface for sending emails.
|
|
1808
|
-
*/
|
|
1809
|
-
interface BaseMailService {
|
|
1810
|
-
/** Sends an email. Returns null if mail service is not configured */
|
|
1811
|
-
send(options: SendMailOptions): Promise<SendMailResult | null>;
|
|
1812
|
-
/** Verifies SMTP connection is working */
|
|
1813
|
-
verify(): Promise<boolean>;
|
|
1814
|
-
}
|
|
1815
|
-
/**
|
|
1816
|
-
* Core services available via ctx.services.
|
|
1817
|
-
*
|
|
1818
|
-
* Modules can extend this with additional services.
|
|
1819
|
-
*/
|
|
1820
|
-
interface CoreServices {
|
|
1821
|
-
/** Error logging and reporting service */
|
|
1822
|
-
logger?: LoggerReporter;
|
|
1823
|
-
/** User management service */
|
|
1824
|
-
users?: BaseUsersService;
|
|
1825
|
-
/** Email sending service */
|
|
1826
|
-
mail?: BaseMailService;
|
|
1827
|
-
/** Extensible: additional services registered by modules */
|
|
1828
|
-
[key: string]: unknown;
|
|
1829
|
-
}
|
|
1830
|
-
/**
|
|
1831
|
-
* Base interface for all entity services.
|
|
1832
|
-
*
|
|
1833
|
-
* Provides read operations common to all entity types.
|
|
1834
|
-
*/
|
|
1835
|
-
interface BaseEntityService<T = unknown> {
|
|
1836
|
-
/** Gets paginated list of records with optional filtering/sorting */
|
|
1837
|
-
findAll(query?: EntityQuery): Promise<PaginatedResult<T>>;
|
|
1838
|
-
/** Gets a single record by ID. Returns null if not found */
|
|
1839
|
-
findById(id: string): Promise<T | null>;
|
|
1840
|
-
/** The entity definition this service operates on */
|
|
1841
|
-
readonly definition: EntityDefinition;
|
|
1842
|
-
}
|
|
1843
|
-
/**
|
|
1844
|
-
* Service for Collection entities with full CRUD operations.
|
|
1845
|
-
*
|
|
1846
|
-
* @see {@link CollectionEntityDefinition}
|
|
1847
|
-
*/
|
|
1848
|
-
interface CollectionEntityService<T = unknown> extends BaseEntityService<T> {
|
|
1849
|
-
/** Creates a new record. userId is used for audit fields */
|
|
1850
|
-
create(data: Partial<T>, userId?: string): Promise<T>;
|
|
1851
|
-
/** Updates an existing record by ID */
|
|
1852
|
-
update(id: string, data: Partial<T>, userId?: string): Promise<T>;
|
|
1853
|
-
/** Deletes a record by ID (soft delete if configured) */
|
|
1854
|
-
delete(id: string): Promise<void>;
|
|
1855
|
-
}
|
|
1856
|
-
/**
|
|
1857
|
-
* Service for Temp entities with automatic TTL expiration.
|
|
1858
|
-
*
|
|
1859
|
-
* @see {@link TempEntityDefinition}
|
|
1860
|
-
*/
|
|
1861
|
-
interface TempEntityService<T = unknown> extends BaseEntityService<T> {
|
|
1862
|
-
/** Creates a temporary record with automatic expiration */
|
|
1863
|
-
create(data: Partial<T>): Promise<T>;
|
|
1864
|
-
/** Updates a record. Optionally extends TTL from current time */
|
|
1865
|
-
update(id: string, data: Partial<T>, extendTtl?: boolean): Promise<T>;
|
|
1866
|
-
/** Deletes a record immediately */
|
|
1867
|
-
delete(id: string): Promise<void>;
|
|
1868
|
-
/** Extends the TTL of a record by additional seconds */
|
|
1869
|
-
extendTtl(id: string, additionalSeconds?: number): Promise<T>;
|
|
1870
|
-
/** Removes all expired records. Returns count of deleted records */
|
|
1871
|
-
cleanup(): Promise<number>;
|
|
1872
|
-
/** Gets remaining TTL in seconds. Returns null if expired or not found */
|
|
1873
|
-
getRemainingTtl(id: string): Promise<number | null>;
|
|
1874
|
-
/** Checks if a record exists and hasn't expired */
|
|
1875
|
-
isValid(id: string): Promise<boolean>;
|
|
1876
|
-
}
|
|
1877
|
-
/**
|
|
1878
|
-
* Service for Event entities (append-only audit logs).
|
|
1879
|
-
*
|
|
1880
|
-
* @see {@link EventEntityDefinition}
|
|
1881
|
-
*/
|
|
1882
|
-
interface EventEntityService<T = unknown> extends BaseEntityService<T> {
|
|
1883
|
-
/** Creates a new event record (append-only) */
|
|
1884
|
-
create(data: Partial<T>): Promise<T>;
|
|
1885
|
-
/** Alias for create - appends a new event */
|
|
1886
|
-
append(data: Partial<T>): Promise<T>;
|
|
1887
|
-
/** Runs retention policy cleanup. Returns count of deleted records */
|
|
1888
|
-
runRetentionCleanup(): Promise<number>;
|
|
1889
|
-
/** Queries events within a date range */
|
|
1890
|
-
findByDateRange(start: Date, end: Date, query?: EntityQuery): Promise<PaginatedResult<T>>;
|
|
1891
|
-
}
|
|
1892
|
-
/**
|
|
1893
|
-
* Service for Single entities (global configuration).
|
|
1894
|
-
*
|
|
1895
|
-
* @see {@link SingleEntityDefinition}
|
|
1896
|
-
*/
|
|
1897
|
-
interface SingleEntityService<T = unknown> extends BaseEntityService<T> {
|
|
1898
|
-
/** Gets the single record (creates with defaults if not exists) */
|
|
1899
|
-
get(): Promise<T | null>;
|
|
1900
|
-
/** Updates the single record */
|
|
1901
|
-
update(data: Partial<T>, userId?: string): Promise<T>;
|
|
1902
|
-
}
|
|
1903
|
-
/**
|
|
1904
|
-
* Service for Config entities (scoped key-value configuration).
|
|
1905
|
-
*
|
|
1906
|
-
* @see {@link ConfigEntityDefinition}
|
|
1907
|
-
*/
|
|
1908
|
-
interface ConfigEntityService<T = unknown> extends BaseEntityService<T> {
|
|
1909
|
-
/** Gets configuration value by key */
|
|
1910
|
-
get(key: string): Promise<T | null>;
|
|
1911
|
-
/** Sets configuration value for a key */
|
|
1912
|
-
set(key: string, value: unknown, userId?: string): Promise<T>;
|
|
1913
|
-
/** Gets all configuration as key-value object */
|
|
1914
|
-
getAll(): Promise<Record<string, unknown>>;
|
|
1915
|
-
}
|
|
1916
|
-
/**
|
|
1917
|
-
* Service for Reference entities (read-only catalogs).
|
|
1918
|
-
* @see {@link ReferenceEntityDefinition}
|
|
1919
|
-
*/
|
|
1920
|
-
type ReferenceEntityService<T = unknown> = BaseEntityService<T>;
|
|
1921
|
-
/**
|
|
1922
|
-
* Service for View entities (SQL views, read-only).
|
|
1923
|
-
* @see {@link ViewEntityDefinition}
|
|
1924
|
-
*/
|
|
1925
|
-
type ViewEntityService<T = unknown> = BaseEntityService<T>;
|
|
1926
|
-
/**
|
|
1927
|
-
* Service for Computed entities (dynamically calculated data).
|
|
1928
|
-
* @see {@link ComputedEntityDefinition}
|
|
1929
|
-
*/
|
|
1930
|
-
type ComputedEntityService<T = unknown> = BaseEntityService<T>;
|
|
1931
|
-
/**
|
|
1932
|
-
* Service for External entities (external API data).
|
|
1933
|
-
* @see {@link ExternalEntityDefinition}
|
|
1934
|
-
*/
|
|
1935
|
-
type ExternalEntityService<T = unknown> = BaseEntityService<T>;
|
|
1936
|
-
/**
|
|
1937
|
-
* Service for Virtual entities (aggregated from multiple sources).
|
|
1938
|
-
* @see {@link VirtualEntityDefinition}
|
|
1939
|
-
*/
|
|
1940
|
-
type VirtualEntityService<T = unknown> = BaseEntityService<T>;
|
|
1941
|
-
/**
|
|
1942
|
-
* Service for Action entities (command execution).
|
|
1943
|
-
*
|
|
1944
|
-
* @see {@link ActionEntityDefinition}
|
|
1945
|
-
*/
|
|
1946
|
-
interface ActionEntityService<T = unknown> {
|
|
1947
|
-
/** Executes the action with provided input data */
|
|
1948
|
-
execute(data: Partial<T>): Promise<unknown>;
|
|
1949
|
-
/** The action definition this service implements */
|
|
1950
|
-
readonly definition: EntityDefinition;
|
|
1951
|
-
}
|
|
1952
|
-
/**
|
|
1953
|
-
* Hooks to customize entity service behavior.
|
|
1954
|
-
* Allows plugins to add logic without inheritance.
|
|
1955
|
-
*/
|
|
1956
|
-
interface EntityServiceHooks<T = unknown> {
|
|
1957
|
-
/**
|
|
1958
|
-
* Runs before creating a record.
|
|
1959
|
-
* @param data Data to insert
|
|
1960
|
-
* @returns Modified data
|
|
1961
|
-
*/
|
|
1962
|
-
beforeCreate?: (data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
|
|
1963
|
-
/**
|
|
1964
|
-
* Runs after creating a record.
|
|
1965
|
-
* @param record Created record
|
|
1966
|
-
*/
|
|
1967
|
-
afterCreate?: (record: T) => Promise<void> | void;
|
|
1968
|
-
/**
|
|
1969
|
-
* Runs before updating a record.
|
|
1970
|
-
* @param id Record ID
|
|
1971
|
-
* @param data Data to update
|
|
1972
|
-
* @returns Modified data
|
|
1973
|
-
*/
|
|
1974
|
-
beforeUpdate?: (id: string, data: Partial<T>) => Promise<Partial<T>> | Partial<T>;
|
|
1975
|
-
/**
|
|
1976
|
-
* Runs after updating a record.
|
|
1977
|
-
* @param record Updated record
|
|
1978
|
-
*/
|
|
1979
|
-
afterUpdate?: (record: T) => Promise<void> | void;
|
|
1980
|
-
/**
|
|
1981
|
-
* Runs before deleting a record.
|
|
1982
|
-
* @param id Record ID
|
|
1983
|
-
*/
|
|
1984
|
-
beforeDelete?: (id: string) => Promise<void> | void;
|
|
1985
|
-
/**
|
|
1986
|
-
* Runs after deleting a record.
|
|
1987
|
-
* @param id Deleted ID
|
|
1988
|
-
*/
|
|
1989
|
-
afterDelete?: (id: string) => Promise<void> | void;
|
|
1990
|
-
/**
|
|
1991
|
-
* Runs after getting a record by ID.
|
|
1992
|
-
* @param record Retrieved record (or null)
|
|
1993
|
-
* @returns Modified record
|
|
1994
|
-
*/
|
|
1995
|
-
afterFindById?: (record: T | null) => Promise<T | null> | T | null;
|
|
1996
|
-
/**
|
|
1997
|
-
* Runs after retrieving a paginated list.
|
|
1998
|
-
* @param result Paginated result
|
|
1999
|
-
* @returns Modified result
|
|
2000
|
-
*/
|
|
2001
|
-
afterFindAll?: (result: PaginatedResult<T>) => Promise<PaginatedResult<T>> | PaginatedResult<T>;
|
|
2002
|
-
}
|
|
2003
|
-
/**
|
|
2004
|
-
* Options for createEntityService
|
|
2005
|
-
*/
|
|
2006
|
-
interface CreateEntityServiceOptions<T = unknown> {
|
|
2007
|
-
/** Hooks to customize behavior */
|
|
2008
|
-
hooks?: EntityServiceHooks<T>;
|
|
2009
|
-
}
|
|
2010
|
-
/**
|
|
2011
|
-
* Express route handler type for entity operations.
|
|
2012
|
-
*/
|
|
2013
|
-
type EntityHandler = (req: Request, res: Response) => Promise<void>;
|
|
2014
|
-
/**
|
|
2015
|
-
* Entity controller with CRUD operation handlers.
|
|
2016
|
-
*
|
|
2017
|
-
* Generated by `createEntityController` with built-in CASL authorization.
|
|
2018
|
-
*/
|
|
2019
|
-
interface EntityController {
|
|
2020
|
-
/** GET / - List all records with pagination */
|
|
2021
|
-
list: EntityHandler;
|
|
2022
|
-
/** GET /:id - Get single record by ID */
|
|
2023
|
-
get: EntityHandler;
|
|
2024
|
-
/** POST / - Create new record (collection entities) */
|
|
2025
|
-
create?: EntityHandler;
|
|
2026
|
-
/** PUT /:id - Update existing record */
|
|
2027
|
-
update?: EntityHandler;
|
|
2028
|
-
/** DELETE /:id - Delete record */
|
|
2029
|
-
delete?: EntityHandler;
|
|
2030
|
-
/** POST /:id/:action - Execute entity action */
|
|
2031
|
-
execute?: EntityHandler;
|
|
2032
|
-
}
|
|
2033
|
-
/**
|
|
2034
|
-
* Module context providing access to all Nexus services and utilities.
|
|
2035
|
-
*
|
|
2036
|
-
* Injected into module lifecycle functions (init, routes, migrate, seed).
|
|
2037
|
-
*
|
|
2038
|
-
* @example
|
|
2039
|
-
* ```typescript
|
|
2040
|
-
* const myModule: ModuleManifest = {
|
|
2041
|
-
* name: 'my-module',
|
|
2042
|
-
* routes: (ctx) => {
|
|
2043
|
-
* const router = ctx.createRouter()
|
|
2044
|
-
* router.get('/hello', (req, res) => res.json({ message: 'Hello!' }))
|
|
2045
|
-
* return router
|
|
2046
|
-
* }
|
|
2047
|
-
* }
|
|
2048
|
-
* ```
|
|
2049
|
-
*/
|
|
2050
|
-
interface ModuleContext {
|
|
2051
|
-
/** Knex database connection for queries and transactions */
|
|
2052
|
-
db: Knex;
|
|
2053
|
-
/** Pino logger instance for structured logging */
|
|
2054
|
-
logger: Logger;
|
|
2055
|
-
/** Helper utilities for migrations and common operations */
|
|
2056
|
-
helpers: ContextHelpers;
|
|
2057
|
-
/** Cryptographic utilities (password hashing) */
|
|
2058
|
-
crypto: ContextCrypto;
|
|
2059
|
-
/** Socket.IO API for real-time communication */
|
|
2060
|
-
socket: ContextSocket;
|
|
2061
|
-
/** Engine API for module/plugin introspection */
|
|
2062
|
-
engine: ContextEngine;
|
|
2063
|
-
/** Factory for creating Express routers */
|
|
2064
|
-
createRouter: () => Router;
|
|
2065
|
-
/** Middleware factories (validate, rateLimit, etc.) */
|
|
2066
|
-
middleware: ModuleMiddlewares;
|
|
2067
|
-
/** Resolved application configuration from env and config files */
|
|
2068
|
-
config: Record<string, unknown>;
|
|
2069
|
-
/** HTTP error constructors for consistent error responses */
|
|
2070
|
-
errors: {
|
|
2071
|
-
/** Generic application error with status code */
|
|
2072
|
-
AppError: new (message: string, statusCode?: number, details?: unknown) => Error & {
|
|
2073
|
-
details?: unknown;
|
|
2074
|
-
};
|
|
2075
|
-
/** 404 Not Found error */
|
|
2076
|
-
NotFoundError: new (message?: string) => Error;
|
|
2077
|
-
/** 401 Unauthorized error */
|
|
2078
|
-
UnauthorizedError: new (message?: string) => Error;
|
|
2079
|
-
/** 403 Forbidden error */
|
|
2080
|
-
ForbiddenError: new (message?: string) => Error;
|
|
2081
|
-
/** 409 Conflict error */
|
|
2082
|
-
ConflictError: new (message?: string) => Error;
|
|
2083
|
-
/** 400 Validation error with field details */
|
|
2084
|
-
ValidationError: new (message?: string, details?: ValidationDetail[]) => Error & {
|
|
2085
|
-
details: ValidationDetail[];
|
|
2086
|
-
};
|
|
2087
|
-
};
|
|
2088
|
-
/** CASL abilities API for authorization */
|
|
2089
|
-
abilities: ModuleAbilities;
|
|
2090
|
-
/** Event emitter for inter-module communication */
|
|
2091
|
-
events: EventEmitterLike;
|
|
2092
|
-
/** Registers a service in the internal registry */
|
|
2093
|
-
registerService(name: string, service: unknown): void;
|
|
2094
|
-
/** Gets a service from the registry with type safety (throws if not found) */
|
|
2095
|
-
getService<T = unknown>(serviceName: string): T;
|
|
2096
|
-
/** Gets an optional service from the registry (returns undefined if not found) */
|
|
2097
|
-
getOptionalService<T = unknown>(serviceName: string): T | undefined;
|
|
2098
|
-
/** Checks if a service is available without throwing */
|
|
2099
|
-
hasService(serviceName: string): boolean;
|
|
2100
|
-
/** Creates entity service based on definition type (auto-detects) */
|
|
2101
|
-
createEntityService<T = unknown>(definition: EntityDefinition, options?: CreateEntityServiceOptions<T>): BaseEntityService<T>;
|
|
2102
|
-
/** Creates entity controller with CASL authorization and validation */
|
|
2103
|
-
createEntityController(service: BaseEntityService, definition: EntityDefinition): EntityController;
|
|
2104
|
-
/** Creates Express router with standard CRUD routes */
|
|
2105
|
-
createEntityRouter(controller: EntityController, definition: EntityDefinition): Router;
|
|
2106
|
-
}
|
|
2107
|
-
/**
|
|
2108
|
-
* Module manifest defining a Nexus module.
|
|
2109
|
-
*
|
|
2110
|
-
* Modules are the core building blocks of Nexus applications.
|
|
2111
|
-
* Each module can define entities, routes, migrations, and services.
|
|
2112
|
-
*
|
|
2113
|
-
* @example
|
|
2114
|
-
* ```typescript
|
|
2115
|
-
* const postsModule: ModuleManifest = {
|
|
2116
|
-
* name: 'posts',
|
|
2117
|
-
* label: { en: 'Posts', es: 'Publicaciones' },
|
|
2118
|
-
* icon: 'mdi:post',
|
|
2119
|
-
* category: 'content',
|
|
2120
|
-
* definitions: [postsEntity, categoriesEntity],
|
|
2121
|
-
* routes: (ctx) => {
|
|
2122
|
-
* const router = ctx.createRouter()
|
|
2123
|
-
* // Custom routes here
|
|
2124
|
-
* return router
|
|
2125
|
-
* }
|
|
2126
|
-
* }
|
|
2127
|
-
* ```
|
|
2128
|
-
*/
|
|
2129
|
-
interface ModuleManifest {
|
|
2130
|
-
/** Unique module identifier (e.g., 'users', 'posts', 'cms') */
|
|
2131
|
-
name: string;
|
|
2132
|
-
/** Display name for UI (singular). Optional if module belongs to a plugin */
|
|
2133
|
-
label?: LocalizedString;
|
|
2134
|
-
/** Display name for UI (plural, used in menus and lists) */
|
|
2135
|
-
labelPlural?: LocalizedString;
|
|
2136
|
-
/** Iconify MDI icon identifier (e.g., 'mdi:account-group') */
|
|
2137
|
-
icon?: string;
|
|
2138
|
-
/** Module description for documentation */
|
|
2139
|
-
description?: LocalizedString;
|
|
2140
|
-
/** Module type: 'core' (built-in), 'plugin' (from plugin), 'auth-plugin', or 'custom' */
|
|
2141
|
-
type?: 'core' | 'plugin' | 'auth-plugin' | 'custom';
|
|
2142
|
-
/** Category for sidebar grouping */
|
|
2143
|
-
category: Category;
|
|
2144
|
-
/** Other module names that must be loaded before this one */
|
|
2145
|
-
dependencies?: string[];
|
|
2146
|
-
/** Requirements that must be met to enable this module */
|
|
2147
|
-
required?: ModuleRequirements;
|
|
2148
|
-
/** Database migration function. Usually auto-generated from entity definitions */
|
|
2149
|
-
migrate?: (ctx: ModuleContext) => Promise<void>;
|
|
2150
|
-
/** Seed function for initial/reference data */
|
|
2151
|
-
seed?: (ctx: ModuleContext) => Promise<void>;
|
|
2152
|
-
/** Initialization function called on app startup */
|
|
2153
|
-
init?: (ctx: ModuleContext) => void;
|
|
2154
|
-
/** Factory function returning Express router with custom routes */
|
|
2155
|
-
routes?: (ctx: ModuleContext) => Router;
|
|
2156
|
-
/** API route prefix. Default: `/{name}` */
|
|
2157
|
-
routePrefix?: string;
|
|
2158
|
-
/** Entity definitions - single source of truth for DB, validation, UI, and CASL */
|
|
2159
|
-
definitions?: EntityDefinition[];
|
|
2160
|
-
}
|
|
2161
|
-
/**
|
|
2162
|
-
* Available categories for organizing modules in the UI sidebar.
|
|
2163
|
-
*
|
|
2164
|
-
* @see {@link CATEGORIES} for metadata (labels, icons, order)
|
|
2165
|
-
*/
|
|
2166
|
-
type Category = 'content' | 'data' | 'assets' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'security' | 'legal' | 'settings';
|
|
2167
|
-
/**
|
|
2168
|
-
* Metadata for a category including display information.
|
|
2169
|
-
*/
|
|
2170
|
-
interface CategoryMeta {
|
|
2171
|
-
/** Localized display label */
|
|
2172
|
-
label: LocalizedString;
|
|
2173
|
-
/** Iconify MDI icon identifier */
|
|
2174
|
-
icon: string;
|
|
2175
|
-
/** Sort order in sidebar (lower = first) */
|
|
2176
|
-
order: number;
|
|
2177
|
-
}
|
|
2178
|
-
/**
|
|
2179
|
-
* Registry of all categories with their metadata.
|
|
2180
|
-
*
|
|
2181
|
-
* Used by the UI to render the sidebar navigation.
|
|
2182
|
-
*/
|
|
2183
|
-
declare const CATEGORIES: Record<Category, CategoryMeta>;
|
|
2184
|
-
/**
|
|
2185
|
-
* Categories sorted by their display order for sidebar rendering.
|
|
11
|
+
* API DTOs (serializable versions for frontend consumption)
|
|
2186
12
|
*/
|
|
2187
|
-
|
|
2188
|
-
/**
|
|
2189
|
-
* Plugin manifest defining a distributable Nexus plugin package.
|
|
2190
|
-
*
|
|
2191
|
-
* Plugins bundle one or more modules into a reusable package.
|
|
2192
|
-
* Distributed via npm (e.g., @gzl10/nexus-plugin-cms).
|
|
2193
|
-
*
|
|
2194
|
-
* @example
|
|
2195
|
-
* ```typescript
|
|
2196
|
-
* const cmsPlugin: PluginManifest = {
|
|
2197
|
-
* name: '@gzl10/nexus-plugin-cms',
|
|
2198
|
-
* code: 'CMS',
|
|
2199
|
-
* label: { en: 'CMS', es: 'CMS' },
|
|
2200
|
-
* category: 'content',
|
|
2201
|
-
* version: '1.0.0',
|
|
2202
|
-
* description: { en: 'Content Management System', es: 'Sistema de Gestión de Contenidos' },
|
|
2203
|
-
* modules: [postsModule, categoriesModule, tagsModule]
|
|
2204
|
-
* }
|
|
2205
|
-
* ```
|
|
2206
|
-
*/
|
|
2207
|
-
interface PluginManifest {
|
|
2208
|
-
/** Plugin npm package name (e.g., '@gzl10/nexus-plugin-cms') */
|
|
2209
|
-
name: string;
|
|
2210
|
-
/** Short code prefix for tables (3-4 uppercase chars, e.g., 'CMS'). Applied to all modules */
|
|
2211
|
-
code: string;
|
|
2212
|
-
/** Display label (singular). Inherited by modules without explicit label */
|
|
2213
|
-
label: LocalizedString;
|
|
2214
|
-
/** Display label (plural, for menus) */
|
|
2215
|
-
labelPlural?: LocalizedString;
|
|
2216
|
-
/** Iconify MDI icon. Inherited by modules without explicit icon */
|
|
2217
|
-
icon?: string;
|
|
2218
|
-
/** Category for sidebar grouping. Inherited by modules without explicit category */
|
|
2219
|
-
category: Category;
|
|
2220
|
-
/** Plugin version following SemVer (e.g., '1.0.0') */
|
|
2221
|
-
version: string;
|
|
2222
|
-
/** Plugin description for documentation */
|
|
2223
|
-
description: LocalizedString;
|
|
2224
|
-
/** Modules bundled in this plugin */
|
|
2225
|
-
modules: ModuleManifest[];
|
|
2226
|
-
}
|
|
13
|
+
|
|
2227
14
|
/**
|
|
2228
15
|
* Serializable field definition for API responses.
|
|
2229
|
-
*
|
|
2230
|
-
* Unlike {@link FieldDefinition}, this contains no functions (ConditionalBoolean
|
|
2231
|
-
* resolved to static boolean). Some fields may be omitted for security.
|
|
2232
|
-
*
|
|
2233
|
-
* @see {@link FieldDefinition} for the full server-side definition
|
|
2234
16
|
*/
|
|
2235
17
|
interface FieldDefinitionDTO {
|
|
2236
|
-
/** Field identifier */
|
|
2237
18
|
name: string;
|
|
2238
|
-
/** Display label */
|
|
2239
19
|
label: LocalizedString;
|
|
2240
|
-
/** Input type for form rendering */
|
|
2241
20
|
input: InputType;
|
|
2242
|
-
/** Placeholder text */
|
|
2243
21
|
placeholder?: LocalizedString;
|
|
2244
|
-
/** Help text */
|
|
2245
22
|
hint?: LocalizedString;
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
disabled?: boolean;
|
|
2250
|
-
/** Whether field is required (resolved from ConditionalBoolean) */
|
|
2251
|
-
required?: boolean;
|
|
2252
|
-
/** Default value for form creation */
|
|
23
|
+
hidden?: boolean | FieldCondition;
|
|
24
|
+
disabled?: boolean | FieldCondition;
|
|
25
|
+
required?: boolean | FieldCondition;
|
|
2253
26
|
defaultValue?: unknown;
|
|
2254
|
-
/** Database config (may be partially omitted for security) */
|
|
2255
27
|
db?: {
|
|
2256
28
|
type: string;
|
|
2257
29
|
nullable?: boolean;
|
|
@@ -2259,14 +31,12 @@ interface FieldDefinitionDTO {
|
|
|
2259
31
|
unique?: boolean;
|
|
2260
32
|
index?: boolean;
|
|
2261
33
|
};
|
|
2262
|
-
/** Relationship config (table name may be omitted for security) */
|
|
2263
34
|
relation?: {
|
|
2264
35
|
table?: string;
|
|
2265
36
|
column?: string;
|
|
2266
37
|
labelField?: string;
|
|
2267
38
|
onDelete?: string;
|
|
2268
39
|
};
|
|
2269
|
-
/** Validation rules */
|
|
2270
40
|
validation?: {
|
|
2271
41
|
min?: number;
|
|
2272
42
|
max?: number;
|
|
@@ -2274,7 +44,6 @@ interface FieldDefinitionDTO {
|
|
|
2274
44
|
format?: string;
|
|
2275
45
|
enum?: string[];
|
|
2276
46
|
};
|
|
2277
|
-
/** Select/dropdown options configuration */
|
|
2278
47
|
options?: {
|
|
2279
48
|
endpoint?: string;
|
|
2280
49
|
valueField?: string;
|
|
@@ -2282,10 +51,10 @@ interface FieldDefinitionDTO {
|
|
|
2282
51
|
static?: Array<{
|
|
2283
52
|
value: string;
|
|
2284
53
|
label: LocalizedString;
|
|
54
|
+
icon?: string;
|
|
2285
55
|
}>;
|
|
2286
56
|
allowCreate?: boolean;
|
|
2287
57
|
};
|
|
2288
|
-
/** Storage configuration for file uploads */
|
|
2289
58
|
storage?: {
|
|
2290
59
|
accept?: string;
|
|
2291
60
|
maxSize?: number;
|
|
@@ -2296,145 +65,461 @@ interface FieldDefinitionDTO {
|
|
|
2296
65
|
height: number;
|
|
2297
66
|
}>;
|
|
2298
67
|
dedupe?: boolean;
|
|
68
|
+
isPublic?: boolean;
|
|
2299
69
|
};
|
|
2300
|
-
/** Field metadata */
|
|
2301
70
|
meta?: FieldMeta;
|
|
2302
|
-
/** Props for input components */
|
|
2303
71
|
inputProps?: Record<string, unknown>;
|
|
2304
|
-
/** Props for display components */
|
|
2305
72
|
displayProps?: Record<string, unknown>;
|
|
2306
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Serializable action definition for API responses.
|
|
76
|
+
*/
|
|
77
|
+
interface ActionDefinitionDTO {
|
|
78
|
+
key: string;
|
|
79
|
+
label: LocalizedString;
|
|
80
|
+
icon?: string;
|
|
81
|
+
scope?: 'module' | 'entity' | 'row';
|
|
82
|
+
method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
83
|
+
fields?: Record<string, FieldDefinitionDTO>;
|
|
84
|
+
order?: number;
|
|
85
|
+
/** Whether this is a batch action with SSE progress */
|
|
86
|
+
batch?: boolean;
|
|
87
|
+
/** Timeout in ms for batch actions */
|
|
88
|
+
batchTimeout?: number;
|
|
89
|
+
/** Whether to show action result in a modal */
|
|
90
|
+
showResult?: boolean;
|
|
91
|
+
}
|
|
2307
92
|
/**
|
|
2308
93
|
* Serializable entity definition for API responses.
|
|
2309
|
-
*
|
|
2310
|
-
* Flattened structure without discriminated union.
|
|
2311
|
-
* Sensitive information may be omitted for security.
|
|
2312
|
-
*
|
|
2313
|
-
* @see {@link EntityDefinition} for the full server-side definition
|
|
2314
94
|
*/
|
|
2315
95
|
interface EntityDefinitionDTO {
|
|
2316
|
-
/** Unique entity identifier (auto-generated at registration) */
|
|
2317
96
|
id: string;
|
|
2318
|
-
/** Table name (may be omitted for security) */
|
|
2319
97
|
table?: string;
|
|
2320
|
-
/** Key for single entities */
|
|
2321
98
|
key?: string;
|
|
2322
|
-
/** Entity type */
|
|
2323
99
|
type: EntityType;
|
|
2324
|
-
/** Display label (singular) */
|
|
2325
100
|
label: LocalizedString;
|
|
2326
|
-
/** Display label (plural) */
|
|
2327
101
|
labelPlural?: LocalizedString;
|
|
2328
|
-
/** Field used as display label */
|
|
2329
102
|
labelField?: string;
|
|
2330
|
-
/** Iconify icon */
|
|
2331
103
|
icon?: string;
|
|
2332
|
-
/** API route prefix */
|
|
2333
104
|
routePrefix?: string;
|
|
2334
|
-
/** UI sort order */
|
|
2335
105
|
order?: number;
|
|
2336
|
-
/** Field definitions */
|
|
2337
106
|
fields: Record<string, FieldDefinitionDTO>;
|
|
2338
|
-
/** Total field count */
|
|
2339
107
|
fieldsCount: number;
|
|
2340
|
-
/** Has created_at/updated_at columns */
|
|
2341
108
|
hasTimestamps: boolean;
|
|
2342
|
-
/** Has created_by/updated_by columns */
|
|
2343
109
|
hasAudit: boolean;
|
|
2344
|
-
/** CASL subject name */
|
|
2345
110
|
caslSubject?: string;
|
|
2346
|
-
/** Returns single record instead of array */
|
|
2347
111
|
isSingle?: boolean;
|
|
2348
|
-
/** UI display mode */
|
|
2349
112
|
displayMode?: DisplayMode;
|
|
2350
|
-
|
|
113
|
+
defaultDisplayMode?: DisplayMode;
|
|
114
|
+
availableDisplayModes?: DisplayMode[];
|
|
115
|
+
hasSearchableFields?: boolean;
|
|
116
|
+
hasSortableFields?: boolean;
|
|
117
|
+
sortableFieldOptions?: Array<{
|
|
118
|
+
value: string;
|
|
119
|
+
label: string;
|
|
120
|
+
}>;
|
|
121
|
+
defaultSort?: {
|
|
122
|
+
field: string;
|
|
123
|
+
order: 'asc' | 'desc';
|
|
124
|
+
};
|
|
2351
125
|
allowCreate?: boolean;
|
|
2352
|
-
/** Allow editing in UI */
|
|
2353
126
|
allowEdit?: boolean;
|
|
2354
|
-
|
|
127
|
+
allowDelete?: boolean;
|
|
2355
128
|
hidden?: boolean;
|
|
129
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
130
|
+
/** Whether this standalone action entity is a batch action with SSE progress */
|
|
131
|
+
batch?: boolean;
|
|
132
|
+
/** Timeout in ms for batch standalone action entities */
|
|
133
|
+
batchTimeout?: number;
|
|
134
|
+
actions?: ActionDefinitionDTO[];
|
|
2356
135
|
}
|
|
2357
136
|
/**
|
|
2358
137
|
* Serializable module for API responses.
|
|
2359
138
|
*/
|
|
2360
139
|
interface ModuleDTO {
|
|
2361
|
-
/** Module identifier */
|
|
2362
140
|
name: string;
|
|
2363
|
-
/** Display label */
|
|
2364
141
|
label: LocalizedString;
|
|
2365
|
-
/** Display label (plural) */
|
|
2366
142
|
labelPlural?: LocalizedString;
|
|
2367
|
-
/** Iconify icon */
|
|
2368
143
|
icon?: string;
|
|
2369
|
-
/** Module description */
|
|
2370
144
|
description?: LocalizedString;
|
|
2371
|
-
/** Module type */
|
|
2372
145
|
type: string;
|
|
2373
|
-
/** Sidebar category */
|
|
2374
146
|
category?: Category;
|
|
2375
|
-
/** Module dependencies */
|
|
2376
147
|
dependencies: string[];
|
|
2377
|
-
/** API route prefix */
|
|
2378
148
|
routePrefix: string;
|
|
2379
|
-
/** CASL subjects defined by this module */
|
|
2380
149
|
subjects: string[];
|
|
2381
|
-
/** Entity definitions */
|
|
2382
150
|
definitions: EntityDefinitionDTO[];
|
|
2383
|
-
/** Has custom routes */
|
|
2384
151
|
hasRoutes: boolean;
|
|
2385
|
-
/** Has migrations */
|
|
2386
152
|
hasMigrate: boolean;
|
|
2387
|
-
/** Has seed data */
|
|
2388
153
|
hasSeed: boolean;
|
|
2389
|
-
/** Has init function */
|
|
2390
154
|
hasInit: boolean;
|
|
2391
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Setup information DTO (without postInstall function).
|
|
158
|
+
*/
|
|
159
|
+
interface PluginSetupInfoDTO {
|
|
160
|
+
steps?: LocalizedString[];
|
|
161
|
+
docsUrl?: string;
|
|
162
|
+
prerequisites?: LocalizedString[];
|
|
163
|
+
caveats?: LocalizedString[];
|
|
164
|
+
}
|
|
2392
165
|
/**
|
|
2393
166
|
* Serializable plugin for API responses.
|
|
2394
167
|
*/
|
|
2395
168
|
interface PluginDTO {
|
|
2396
|
-
/** Plugin package name */
|
|
2397
169
|
name: string;
|
|
2398
|
-
/** Plugin code prefix */
|
|
2399
170
|
code: string;
|
|
2400
|
-
/** Display label */
|
|
2401
171
|
label: LocalizedString;
|
|
2402
|
-
/** Display label (plural) */
|
|
2403
172
|
labelPlural?: LocalizedString;
|
|
2404
|
-
/** Iconify icon */
|
|
2405
173
|
icon?: string;
|
|
2406
|
-
/** Sidebar category */
|
|
2407
174
|
category?: Category;
|
|
2408
|
-
/** Plugin version */
|
|
2409
175
|
version: string;
|
|
2410
|
-
/** Plugin description */
|
|
2411
176
|
description: LocalizedString;
|
|
2412
|
-
/** Modules in this plugin */
|
|
2413
177
|
modules: ModuleDTO[];
|
|
178
|
+
envVars?: Array<{
|
|
179
|
+
name: string;
|
|
180
|
+
description: LocalizedString;
|
|
181
|
+
required: boolean;
|
|
182
|
+
default?: string;
|
|
183
|
+
example?: string;
|
|
184
|
+
pattern?: string;
|
|
185
|
+
sensitive?: boolean;
|
|
186
|
+
}>;
|
|
187
|
+
peerDependencies?: Record<string, string>;
|
|
188
|
+
setup?: PluginSetupInfoDTO;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Plugin state persisted in database
|
|
192
|
+
*/
|
|
193
|
+
interface PluginStateDTO {
|
|
194
|
+
name: string;
|
|
195
|
+
enabled: boolean;
|
|
196
|
+
installed_at: string;
|
|
197
|
+
enabled_at: string | null;
|
|
198
|
+
disabled_at: string | null;
|
|
199
|
+
installed_version: string;
|
|
200
|
+
installed_by: string | null;
|
|
201
|
+
config: Record<string, unknown>;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Plugin available in npm registry (for store)
|
|
205
|
+
*/
|
|
206
|
+
interface PluginStoreItemDTO {
|
|
207
|
+
name: string;
|
|
208
|
+
version: string;
|
|
209
|
+
description: string;
|
|
210
|
+
keywords: string[];
|
|
211
|
+
repository: string | null;
|
|
212
|
+
compatibility: 'compatible' | 'incompatible' | 'unknown';
|
|
213
|
+
installed: boolean;
|
|
214
|
+
installedVersion: string | null;
|
|
215
|
+
enabled: boolean | null;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Result of install/uninstall action
|
|
219
|
+
*/
|
|
220
|
+
interface PluginActionResult {
|
|
221
|
+
success: boolean;
|
|
222
|
+
message: string;
|
|
223
|
+
restarting?: boolean;
|
|
2414
224
|
}
|
|
2415
225
|
/**
|
|
2416
226
|
* Application manifest DTO (core or user app).
|
|
2417
227
|
*/
|
|
2418
228
|
interface ManifestDTO {
|
|
2419
|
-
/** Application name */
|
|
2420
229
|
name: string;
|
|
2421
|
-
/** Application version */
|
|
2422
230
|
version: string;
|
|
2423
|
-
/** Registered modules */
|
|
2424
231
|
modules: ModuleDTO[];
|
|
2425
|
-
/** Registered plugins */
|
|
2426
232
|
plugins?: PluginDTO[];
|
|
2427
233
|
}
|
|
2428
234
|
/**
|
|
2429
235
|
* Combined manifest DTO with both core and user manifests.
|
|
2430
236
|
*/
|
|
2431
237
|
interface CombinedManifestDTO {
|
|
2432
|
-
/** Core nexus-backend manifest */
|
|
2433
238
|
core: ManifestDTO;
|
|
2434
|
-
/** User project manifest (null in standalone mode) */
|
|
2435
239
|
user: ManifestDTO | null;
|
|
2436
|
-
/** Whether running with a user project */
|
|
2437
240
|
hasUserApp: boolean;
|
|
2438
241
|
}
|
|
2439
242
|
|
|
2440
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Type guards and utilities for EntityDefinitions
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Entities that persist in the local DB with their own table.
|
|
249
|
+
* Excludes: action, external, virtual, computed, single (uses shared table).
|
|
250
|
+
*/
|
|
251
|
+
type PersistentEntityDefinition = CollectionEntityDefinition | ReferenceEntityDefinition | EventEntityDefinition | ConfigEntityDefinition | TempEntityDefinition | ViewEntityDefinition | TreeEntityDefinition | DagEntityDefinition;
|
|
252
|
+
/**
|
|
253
|
+
* Entities without local persistence (read-only or without DB)
|
|
254
|
+
*/
|
|
255
|
+
type NonPersistentEntityDefinition = (ActionDefinition & {
|
|
256
|
+
type: 'action';
|
|
257
|
+
}) | ExternalEntityDefinition | VirtualEntityDefinition | ComputedEntityDefinition;
|
|
258
|
+
/**
|
|
259
|
+
* Type guard to check whether an entity persists in the local DB with its own table
|
|
260
|
+
*/
|
|
261
|
+
declare function isPersistentEntity(entity: EntityDefinition): entity is PersistentEntityDefinition;
|
|
262
|
+
/**
|
|
263
|
+
* Type guard to check whether it is a singleton (uses shared sys_settings table)
|
|
264
|
+
*/
|
|
265
|
+
declare function isSingletonEntity(entity: EntityDefinition): entity is SingleEntityDefinition;
|
|
266
|
+
/**
|
|
267
|
+
* Type guard to check whether an entity has its own table (for migrations)
|
|
268
|
+
*/
|
|
269
|
+
declare function hasTable(entity: EntityDefinition): entity is PersistentEntityDefinition;
|
|
270
|
+
/**
|
|
271
|
+
* Gets an entity name in singular PascalCase.
|
|
272
|
+
* 'cms_posts' → 'Post', 'rol_role_permissions' → 'RolePermission'
|
|
273
|
+
*/
|
|
274
|
+
declare function getEntityName(entity: EntityDefinition): string;
|
|
275
|
+
/**
|
|
276
|
+
* Gets the CASL subject for an entity
|
|
277
|
+
*/
|
|
278
|
+
declare function getEntitySubject(entity: PersistentEntityDefinition): string;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* OIDC Types for Authentication Plugins
|
|
282
|
+
*
|
|
283
|
+
* Shared types for OpenID Connect authentication providers.
|
|
284
|
+
* Used by plugins like @gzl10/nexus-plugin-pocketid and @gzl10/nexus-plugin-google-auth.
|
|
285
|
+
*/
|
|
286
|
+
/**
|
|
287
|
+
* OIDC Discovery Document
|
|
288
|
+
* @see https://openid.net/specs/openid-connect-discovery-1_0.html
|
|
289
|
+
*/
|
|
290
|
+
interface OidcDiscoveryDocument {
|
|
291
|
+
issuer: string;
|
|
292
|
+
authorization_endpoint: string;
|
|
293
|
+
token_endpoint: string;
|
|
294
|
+
userinfo_endpoint: string;
|
|
295
|
+
jwks_uri: string;
|
|
296
|
+
end_session_endpoint?: string;
|
|
297
|
+
scopes_supported?: string[];
|
|
298
|
+
response_types_supported?: string[];
|
|
299
|
+
grant_types_supported?: string[];
|
|
300
|
+
subject_types_supported?: string[];
|
|
301
|
+
id_token_signing_alg_values_supported?: string[];
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* OIDC Tokens from token endpoint
|
|
305
|
+
*/
|
|
306
|
+
interface OidcTokens {
|
|
307
|
+
access_token: string;
|
|
308
|
+
token_type: string;
|
|
309
|
+
expires_in?: number;
|
|
310
|
+
refresh_token?: string;
|
|
311
|
+
id_token?: string;
|
|
312
|
+
scope?: string;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* ID Token claims after validation
|
|
316
|
+
*/
|
|
317
|
+
interface IdTokenClaims {
|
|
318
|
+
iss: string;
|
|
319
|
+
sub: string;
|
|
320
|
+
aud: string | string[];
|
|
321
|
+
exp: number;
|
|
322
|
+
iat: number;
|
|
323
|
+
nonce?: string;
|
|
324
|
+
auth_time?: number;
|
|
325
|
+
azp?: string;
|
|
326
|
+
at_hash?: string;
|
|
327
|
+
email?: string;
|
|
328
|
+
email_verified?: boolean;
|
|
329
|
+
name?: string;
|
|
330
|
+
/** Google Workspace hosted domain */
|
|
331
|
+
hd?: string;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* OIDC UserInfo response
|
|
335
|
+
*/
|
|
336
|
+
interface OidcUserInfo {
|
|
337
|
+
sub: string;
|
|
338
|
+
name?: string;
|
|
339
|
+
given_name?: string;
|
|
340
|
+
family_name?: string;
|
|
341
|
+
preferred_username?: string;
|
|
342
|
+
email?: string;
|
|
343
|
+
email_verified?: boolean;
|
|
344
|
+
picture?: string;
|
|
345
|
+
locale?: string;
|
|
346
|
+
updated_at?: number;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Authorization URL parameters
|
|
350
|
+
*/
|
|
351
|
+
interface AuthorizationParams {
|
|
352
|
+
clientId: string;
|
|
353
|
+
redirectUri: string;
|
|
354
|
+
scopes: string[];
|
|
355
|
+
state: string;
|
|
356
|
+
nonce: string;
|
|
357
|
+
responseType?: string;
|
|
358
|
+
/** Google Workspace hosted domain (hd parameter) */
|
|
359
|
+
hostedDomain?: string;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Token exchange parameters
|
|
363
|
+
*/
|
|
364
|
+
interface TokenExchangeParams {
|
|
365
|
+
tokenEndpoint: string;
|
|
366
|
+
clientId: string;
|
|
367
|
+
clientSecret: string;
|
|
368
|
+
code: string;
|
|
369
|
+
redirectUri: string;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* ID Token validation config
|
|
373
|
+
*/
|
|
374
|
+
interface TokenValidationConfig {
|
|
375
|
+
jwksUri: string;
|
|
376
|
+
issuer: string;
|
|
377
|
+
clientId: string;
|
|
378
|
+
nonce?: string;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* OIDC state stored temporarily during auth flow
|
|
382
|
+
*/
|
|
383
|
+
interface OidcState {
|
|
384
|
+
state: string;
|
|
385
|
+
nonce: string;
|
|
386
|
+
redirectUri: string;
|
|
387
|
+
createdAt: number;
|
|
388
|
+
/** User ID if linking existing account */
|
|
389
|
+
linkUserId?: string;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Session result after successful authentication
|
|
393
|
+
*/
|
|
394
|
+
interface SessionResult {
|
|
395
|
+
accessToken: string;
|
|
396
|
+
refreshToken: string;
|
|
397
|
+
expiresIn: number;
|
|
398
|
+
user: {
|
|
399
|
+
id: string;
|
|
400
|
+
email: string;
|
|
401
|
+
name?: string;
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* OIDC Client interface
|
|
407
|
+
*/
|
|
408
|
+
interface OidcClient {
|
|
409
|
+
/** Discover OIDC endpoints from issuer */
|
|
410
|
+
discover(issuerUrl: string): Promise<OidcDiscoveryDocument>;
|
|
411
|
+
/** Build authorization URL for redirect */
|
|
412
|
+
buildAuthorizationUrl(authorizationEndpoint: string, params: AuthorizationParams): string;
|
|
413
|
+
/** Exchange authorization code for tokens */
|
|
414
|
+
exchangeCode(params: TokenExchangeParams): Promise<OidcTokens>;
|
|
415
|
+
/** Get user info from access token */
|
|
416
|
+
getUserInfo(accessToken: string, userInfoEndpoint: string): Promise<OidcUserInfo>;
|
|
417
|
+
/** Validate ID token using JWKS */
|
|
418
|
+
validateIdToken(idToken: string, config: TokenValidationConfig): Promise<IdTokenClaims>;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Create OIDC client instance
|
|
422
|
+
*/
|
|
423
|
+
declare function createOidcClient(): OidcClient;
|
|
424
|
+
/**
|
|
425
|
+
* Get shared OIDC client instance (singleton)
|
|
426
|
+
*/
|
|
427
|
+
declare function getOidcClient(): OidcClient;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* OIDC State Manager
|
|
431
|
+
*
|
|
432
|
+
* Manages OIDC state/nonce storage with TTL expiration.
|
|
433
|
+
* Used for CSRF protection during authentication flows.
|
|
434
|
+
*
|
|
435
|
+
* Note: In-memory implementation. For production with multiple
|
|
436
|
+
* instances, consider Redis or database storage.
|
|
437
|
+
*/
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* State manager interface
|
|
441
|
+
*/
|
|
442
|
+
interface StateManager<T extends OidcState = OidcState> {
|
|
443
|
+
/** Store state data */
|
|
444
|
+
store(state: string, data: T): void;
|
|
445
|
+
/** Verify and retrieve state (returns null if expired/invalid) */
|
|
446
|
+
verify(state: string): T | null;
|
|
447
|
+
/** Clear state after use */
|
|
448
|
+
clear(state: string): void;
|
|
449
|
+
/** Get TTL in milliseconds */
|
|
450
|
+
getTtl(): number;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Create in-memory state manager
|
|
454
|
+
*
|
|
455
|
+
* @param ttlMs - Time-to-live in milliseconds (default: 10 minutes)
|
|
456
|
+
*/
|
|
457
|
+
declare function createStateManager<T extends OidcState = OidcState>(ttlMs?: number): StateManager<T>;
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Domain Filter for OIDC Authentication
|
|
461
|
+
*
|
|
462
|
+
* Validates email domains against an allowlist.
|
|
463
|
+
* Used to restrict authentication to specific domains.
|
|
464
|
+
*/
|
|
465
|
+
/**
|
|
466
|
+
* Domain filter options
|
|
467
|
+
*/
|
|
468
|
+
interface DomainFilterOptions {
|
|
469
|
+
/** Throw error if domain not allowed (default: true) */
|
|
470
|
+
throwOnForbidden?: boolean;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Domain filter result
|
|
474
|
+
*/
|
|
475
|
+
interface DomainFilterResult {
|
|
476
|
+
allowed: boolean;
|
|
477
|
+
domain: string;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Check if email domain is in allowed list
|
|
481
|
+
*
|
|
482
|
+
* @param email - User email address
|
|
483
|
+
* @param allowedDomainsJson - JSON array of allowed domains (null = all allowed)
|
|
484
|
+
* @returns Domain filter result
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Allow specific domains
|
|
488
|
+
* const result = checkAllowedDomain('user@example.com', '["example.com", "test.com"]')
|
|
489
|
+
* // result: { allowed: true, domain: 'example.com' }
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* // Allow all domains
|
|
493
|
+
* const result = checkAllowedDomain('user@any.com', null)
|
|
494
|
+
* // result: { allowed: true, domain: 'any.com' }
|
|
495
|
+
*/
|
|
496
|
+
declare function checkAllowedDomain(email: string, allowedDomainsJson: string | null): DomainFilterResult;
|
|
497
|
+
/**
|
|
498
|
+
* Assert email domain is allowed (throws if not)
|
|
499
|
+
*
|
|
500
|
+
* @param email - User email address
|
|
501
|
+
* @param allowedDomainsJson - JSON array of allowed domains
|
|
502
|
+
* @param errorClass - Error class to throw (default: Error)
|
|
503
|
+
* @throws Error if domain is not allowed
|
|
504
|
+
*/
|
|
505
|
+
declare function assertAllowedDomain(email: string, allowedDomainsJson: string | null, errorClass?: new (message: string) => Error): void;
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @module nexus-sdk
|
|
509
|
+
* @description SDK types for creating Nexus plugins and modules without full backend dependency
|
|
510
|
+
*
|
|
511
|
+
* @gzl10/nexus-sdk
|
|
512
|
+
*
|
|
513
|
+
* Use this package to define plugin/module manifests without
|
|
514
|
+
* depending on the full @gzl10/nexus-backend package.
|
|
515
|
+
*
|
|
516
|
+
* @remarks
|
|
517
|
+
* Types are organized in submodules under ./types/ for maintainability.
|
|
518
|
+
* This barrel file re-exports everything for backwards compatibility.
|
|
519
|
+
*/
|
|
520
|
+
|
|
521
|
+
type KnexCreateTableBuilder = Knex.CreateTableBuilder;
|
|
522
|
+
type KnexAlterTableBuilder = Knex.AlterTableBuilder;
|
|
523
|
+
type KnexTransaction = Knex.Transaction;
|
|
524
|
+
|
|
525
|
+
export { ActionDefinition, type ActionDefinitionDTO, type AuthorizationParams, Category, CollectionEntityDefinition, type CombinedManifestDTO, ComputedEntityDefinition, ConfigEntityDefinition, DagEntityDefinition, DisplayMode, type DomainFilterOptions, type DomainFilterResult, EntityDefinition, type EntityDefinitionDTO, EntityType, EventEntityDefinition, ExternalEntityDefinition, FieldCondition, type FieldDefinitionDTO, FieldMeta, type IdTokenClaims, InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, LocalizedString, type ManifestDTO, type ModuleDTO, type NonPersistentEntityDefinition, type OidcClient, type OidcDiscoveryDocument, type OidcState, type OidcTokens, type OidcUserInfo, type PersistentEntityDefinition, type PluginActionResult, type PluginDTO, type PluginSetupInfoDTO, type PluginStateDTO, type PluginStoreItemDTO, ReferenceEntityDefinition, type SessionResult, SingleEntityDefinition, type StateManager, TempEntityDefinition, type TokenExchangeParams, type TokenValidationConfig, TreeEntityDefinition, ViewEntityDefinition, VirtualEntityDefinition, assertAllowedDomain, checkAllowedDomain, createOidcClient, createStateManager, getEntityName, getEntitySubject, getOidcClient, hasTable, isPersistentEntity, isSingletonEntity };
|