crudora 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Express } from 'express';
2
2
  import { z } from 'zod';
3
+ import http from 'http';
3
4
 
4
5
  /**
5
6
  * Base class for all Crudora models. Extend this to define your table schema,
@@ -164,6 +165,18 @@ declare class Repository<T extends Model> {
164
165
  transaction<R>(fn: (trx: Repository<T>) => Promise<R>): Promise<R>;
165
166
  }
166
167
 
168
+ interface OpenApiInfo {
169
+ title?: string;
170
+ version?: string;
171
+ description?: string;
172
+ }
173
+ declare class OpenApiGenerator {
174
+ static generate(models: Map<string, ModelConstructor>, customRoutes: Array<{
175
+ method: string;
176
+ path: string;
177
+ }>, basePath: string, info?: OpenApiInfo): Record<string, any>;
178
+ }
179
+
167
180
  type FieldType = 'string' | 'text' | 'integer' | 'number' | 'boolean' | 'date' | 'uuid' | 'decimal' | 'json' | 'enum' | 'bigint' | 'serial' | 'array';
168
181
  interface FieldOptions {
169
182
  type: FieldType;
@@ -240,6 +253,7 @@ declare class Crudora {
240
253
  */
241
254
  getTable<T extends Model>(modelClass: ModelConstructor<T>): any;
242
255
  generateDrizzleSchema(): string;
256
+ generateOpenApiSpec(basePath?: string, info?: OpenApiInfo): Record<string, any>;
243
257
  getValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<Partial<T>>;
244
258
  getStrictValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<T>;
245
259
  get(path: string, ...handlers: Array<(req: any, res: any, next?: any) => void>): this;
@@ -265,6 +279,98 @@ interface RateLimitConfig {
265
279
  */
266
280
  keyGenerator?: (req: any) => string;
267
281
  }
282
+ /**
283
+ * Configuration options forwarded to `@scalar/express-api-reference`.
284
+ * All known options are typed for autocomplete. Any additional option is also
285
+ * accepted via the index signature.
286
+ *
287
+ * Full reference: https://github.com/scalar/scalar/blob/main/documentation/configuration.md
288
+ */
289
+ interface ScalarConfig {
290
+ /**
291
+ * UI color theme.
292
+ * @default 'default'
293
+ */
294
+ theme?: 'default' | 'alternate' | 'moon' | 'purple' | 'solarized' | 'mars' | 'deepSpace' | 'none';
295
+ /**
296
+ * Page layout style.
297
+ * @default 'modern'
298
+ */
299
+ layout?: 'modern' | 'classic';
300
+ /** Enable dark mode by default. */
301
+ darkMode?: boolean;
302
+ /** Force light or dark mode, ignoring system preference. */
303
+ forceDarkModeState?: 'dark' | 'light';
304
+ /** Hide the dark-mode toggle button. */
305
+ hideDarkModeToggle?: boolean;
306
+ /** Hide the model/schema section. */
307
+ hideModels?: boolean;
308
+ /** Hide the "Download" button in the spec header. */
309
+ hideDownloadButton?: boolean;
310
+ /** Hide the "Test Request" (client) button on each operation. */
311
+ hideClientButton?: boolean;
312
+ /** Show or hide the sidebar. @default true */
313
+ showSidebar?: boolean;
314
+ /** Expand all operation tags on load. */
315
+ defaultOpenAllTags?: boolean;
316
+ /** Inject custom CSS into the Scalar page. */
317
+ customCss?: string;
318
+ /** Custom favicon URL or data URI. */
319
+ favicon?: string;
320
+ /** Keyboard shortcut that opens the search dialog. @default 'k' */
321
+ searchHotKey?: string;
322
+ /**
323
+ * Override the server list shown in the UI.
324
+ * Each entry is `{ url: string; description?: string }`.
325
+ */
326
+ servers?: Array<{
327
+ url: string;
328
+ description?: string;
329
+ [key: string]: any;
330
+ }>;
331
+ /**
332
+ * Default values pre-filled in the "Try it out" form — useful for dev tokens.
333
+ * Shape depends on your security scheme; see Scalar docs for details.
334
+ */
335
+ authentication?: Record<string, any>;
336
+ /**
337
+ * HTML `<meta>` / Open Graph values for the docs page.
338
+ * Accepts `title`, `description`, `ogDescription`, `ogTitle`, `ogImage`, `twitterTitle`.
339
+ */
340
+ metaData?: {
341
+ title?: string;
342
+ description?: string;
343
+ ogDescription?: string;
344
+ ogTitle?: string;
345
+ ogImage?: string;
346
+ twitterTitle?: string;
347
+ [key: string]: any;
348
+ };
349
+ /** Use Scalar's default font stack. @default true */
350
+ withDefaultFonts?: boolean;
351
+ /** Allow users to edit the spec inline (read-only by default). */
352
+ isEditable?: boolean;
353
+ /** Any other Scalar option not listed above. */
354
+ [key: string]: any;
355
+ }
356
+ interface DocsConfig {
357
+ /** Path where the UI and spec are served. Default: `'/docs'`. */
358
+ path?: string;
359
+ /** OpenAPI `info.title`. Default: `'Crudora API'`. */
360
+ title?: string;
361
+ /** OpenAPI `info.version`. Default: `'1.0.0'`. */
362
+ version?: string;
363
+ /** OpenAPI `info.description`. */
364
+ description?: string;
365
+ /**
366
+ * Options forwarded directly to `apiReference()` from `@scalar/express-api-reference`.
367
+ * Fully typed for autocomplete — see `ScalarConfig` for all available fields.
368
+ *
369
+ * @example
370
+ * scalar: { theme: 'purple', darkMode: true, layout: 'classic' }
371
+ */
372
+ scalar?: ScalarConfig;
373
+ }
268
374
  interface CrudoraServerConfig {
269
375
  port?: number;
270
376
  /**
@@ -297,20 +403,66 @@ interface CrudoraServerConfig {
297
403
  * - Omit / `undefined` (default): enabled with 100 requests per minute per IP.
298
404
  * - Pass a `RateLimitConfig` object to customise the window, limit, or key function.
299
405
  * - Pass `false` to disable rate limiting entirely (e.g. when using an external proxy-level limiter).
406
+ *
407
+ * **Important:** This limiter is in-memory and per-process. In multi-instance deployments
408
+ * (load balancer, Kubernetes replicas), each instance tracks its own counter independently —
409
+ * the effective limit per client is `max × instanceCount`. Use a Redis-backed limiter
410
+ * (e.g. `rate-limiter-flexible`) and pass `rateLimit: false` to disable this one.
300
411
  */
301
412
  rateLimit?: RateLimitConfig | false;
413
+ /**
414
+ * Socket-level request timeout in milliseconds.
415
+ * Requests that exceed this duration are terminated with a `503` response.
416
+ * - Omit / `0` (default): no timeout.
417
+ * - Recommended: `30_000` (30 s) for most APIs.
418
+ */
419
+ timeout?: number;
420
+ /**
421
+ * Built-in health check endpoint.
422
+ * - `true` (default): mounts `GET /health` returning `{ status: 'ok' }`.
423
+ * - `string`: custom path, e.g. `'/healthz'`.
424
+ * - `false`: disable entirely.
425
+ */
426
+ healthCheck?: boolean | string;
427
+ /**
428
+ * Built-in API documentation powered by Scalar.
429
+ * Requires `@scalar/express-api-reference` to be installed separately.
430
+ *
431
+ * - `false` (default): disabled.
432
+ * - `true`: mount UI at `GET /docs` and spec at `GET /docs/openapi.json`.
433
+ * - `string`: custom base path, e.g. `'/api-docs'`.
434
+ * - `DocsConfig`: full control — path, OpenAPI info, Scalar theme/layout/etc.
435
+ *
436
+ * @example
437
+ * docs: { path: '/docs', title: 'My API', scalar: { theme: 'purple' } }
438
+ */
439
+ docs?: boolean | string | DocsConfig;
302
440
  }
303
441
  declare class CrudoraServer {
304
442
  private app;
305
443
  private crudora;
306
444
  private config;
445
+ private httpServer;
307
446
  constructor(config: CrudoraServerConfig);
308
447
  private setupMiddleware;
309
448
  registerModel(...modelClasses: ModelConstructor[]): this;
310
449
  registerTable<T extends Model>(modelClass: ModelConstructor<T>, table: any): this;
311
450
  generateRoutes(): this;
312
451
  use(middleware: any): this;
313
- listen(callback?: () => void): void;
452
+ /**
453
+ * Starts the HTTP server and returns the underlying `http.Server` instance.
454
+ * Use the returned server for graceful shutdown:
455
+ *
456
+ * @example
457
+ * const httpServer = server.listen();
458
+ * process.on('SIGTERM', () => httpServer.close(() => process.exit(0)));
459
+ */
460
+ listen(callback?: () => void): http.Server;
461
+ /**
462
+ * Returns the `http.Server` instance after `listen()` has been called, or `null` before.
463
+ * Useful when you need the server reference without calling listen again.
464
+ */
465
+ getHttpServer(): http.Server | null;
314
466
  getApp(): Express;
315
467
  getCrudora(): Crudora;
316
468
  /**
@@ -401,4 +553,4 @@ declare function BelongsTo(model: () => any, foreignKey: string, ownerKey?: stri
401
553
  declare function BelongsToMany(model: () => any, pivotModel: () => any, pivotForeignKey: string, pivotRelatedKey: string, localKey?: string): (target: any, propertyKey: string) => void;
402
554
  declare function getRelationMetadata(target: any): Record<string, RelationDefinition>;
403
555
 
404
- export { BelongsTo, BelongsToMany, Crudora, type CrudoraLogger, CrudoraServer, type CrudoraServerConfig, type CursorPaginationOptions, type CursorResult, type Dialect, DrizzleTableBuilder, Field, type FieldOptions, type FieldType, type FindAllOptions, type FindByIdOptions, HasMany, HasOne, Model, type ModelConstructor, type ModelOptions, NotFoundError, type RateLimitConfig, type RelationDefinition, type RelationType, Repository, SchemaGenerator, ValidationGenerator, getFieldMetadata, getRelationMetadata };
556
+ export { BelongsTo, BelongsToMany, Crudora, type CrudoraLogger, CrudoraServer, type CrudoraServerConfig, type CursorPaginationOptions, type CursorResult, type Dialect, type DocsConfig, DrizzleTableBuilder, Field, type FieldOptions, type FieldType, type FindAllOptions, type FindByIdOptions, HasMany, HasOne, Model, type ModelConstructor, type ModelOptions, NotFoundError, OpenApiGenerator, type OpenApiInfo, type RateLimitConfig, type RelationDefinition, type RelationType, Repository, type ScalarConfig, SchemaGenerator, ValidationGenerator, getFieldMetadata, getRelationMetadata };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Express } from 'express';
2
2
  import { z } from 'zod';
3
+ import http from 'http';
3
4
 
4
5
  /**
5
6
  * Base class for all Crudora models. Extend this to define your table schema,
@@ -164,6 +165,18 @@ declare class Repository<T extends Model> {
164
165
  transaction<R>(fn: (trx: Repository<T>) => Promise<R>): Promise<R>;
165
166
  }
166
167
 
168
+ interface OpenApiInfo {
169
+ title?: string;
170
+ version?: string;
171
+ description?: string;
172
+ }
173
+ declare class OpenApiGenerator {
174
+ static generate(models: Map<string, ModelConstructor>, customRoutes: Array<{
175
+ method: string;
176
+ path: string;
177
+ }>, basePath: string, info?: OpenApiInfo): Record<string, any>;
178
+ }
179
+
167
180
  type FieldType = 'string' | 'text' | 'integer' | 'number' | 'boolean' | 'date' | 'uuid' | 'decimal' | 'json' | 'enum' | 'bigint' | 'serial' | 'array';
168
181
  interface FieldOptions {
169
182
  type: FieldType;
@@ -240,6 +253,7 @@ declare class Crudora {
240
253
  */
241
254
  getTable<T extends Model>(modelClass: ModelConstructor<T>): any;
242
255
  generateDrizzleSchema(): string;
256
+ generateOpenApiSpec(basePath?: string, info?: OpenApiInfo): Record<string, any>;
243
257
  getValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<Partial<T>>;
244
258
  getStrictValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<T>;
245
259
  get(path: string, ...handlers: Array<(req: any, res: any, next?: any) => void>): this;
@@ -265,6 +279,98 @@ interface RateLimitConfig {
265
279
  */
266
280
  keyGenerator?: (req: any) => string;
267
281
  }
282
+ /**
283
+ * Configuration options forwarded to `@scalar/express-api-reference`.
284
+ * All known options are typed for autocomplete. Any additional option is also
285
+ * accepted via the index signature.
286
+ *
287
+ * Full reference: https://github.com/scalar/scalar/blob/main/documentation/configuration.md
288
+ */
289
+ interface ScalarConfig {
290
+ /**
291
+ * UI color theme.
292
+ * @default 'default'
293
+ */
294
+ theme?: 'default' | 'alternate' | 'moon' | 'purple' | 'solarized' | 'mars' | 'deepSpace' | 'none';
295
+ /**
296
+ * Page layout style.
297
+ * @default 'modern'
298
+ */
299
+ layout?: 'modern' | 'classic';
300
+ /** Enable dark mode by default. */
301
+ darkMode?: boolean;
302
+ /** Force light or dark mode, ignoring system preference. */
303
+ forceDarkModeState?: 'dark' | 'light';
304
+ /** Hide the dark-mode toggle button. */
305
+ hideDarkModeToggle?: boolean;
306
+ /** Hide the model/schema section. */
307
+ hideModels?: boolean;
308
+ /** Hide the "Download" button in the spec header. */
309
+ hideDownloadButton?: boolean;
310
+ /** Hide the "Test Request" (client) button on each operation. */
311
+ hideClientButton?: boolean;
312
+ /** Show or hide the sidebar. @default true */
313
+ showSidebar?: boolean;
314
+ /** Expand all operation tags on load. */
315
+ defaultOpenAllTags?: boolean;
316
+ /** Inject custom CSS into the Scalar page. */
317
+ customCss?: string;
318
+ /** Custom favicon URL or data URI. */
319
+ favicon?: string;
320
+ /** Keyboard shortcut that opens the search dialog. @default 'k' */
321
+ searchHotKey?: string;
322
+ /**
323
+ * Override the server list shown in the UI.
324
+ * Each entry is `{ url: string; description?: string }`.
325
+ */
326
+ servers?: Array<{
327
+ url: string;
328
+ description?: string;
329
+ [key: string]: any;
330
+ }>;
331
+ /**
332
+ * Default values pre-filled in the "Try it out" form — useful for dev tokens.
333
+ * Shape depends on your security scheme; see Scalar docs for details.
334
+ */
335
+ authentication?: Record<string, any>;
336
+ /**
337
+ * HTML `<meta>` / Open Graph values for the docs page.
338
+ * Accepts `title`, `description`, `ogDescription`, `ogTitle`, `ogImage`, `twitterTitle`.
339
+ */
340
+ metaData?: {
341
+ title?: string;
342
+ description?: string;
343
+ ogDescription?: string;
344
+ ogTitle?: string;
345
+ ogImage?: string;
346
+ twitterTitle?: string;
347
+ [key: string]: any;
348
+ };
349
+ /** Use Scalar's default font stack. @default true */
350
+ withDefaultFonts?: boolean;
351
+ /** Allow users to edit the spec inline (read-only by default). */
352
+ isEditable?: boolean;
353
+ /** Any other Scalar option not listed above. */
354
+ [key: string]: any;
355
+ }
356
+ interface DocsConfig {
357
+ /** Path where the UI and spec are served. Default: `'/docs'`. */
358
+ path?: string;
359
+ /** OpenAPI `info.title`. Default: `'Crudora API'`. */
360
+ title?: string;
361
+ /** OpenAPI `info.version`. Default: `'1.0.0'`. */
362
+ version?: string;
363
+ /** OpenAPI `info.description`. */
364
+ description?: string;
365
+ /**
366
+ * Options forwarded directly to `apiReference()` from `@scalar/express-api-reference`.
367
+ * Fully typed for autocomplete — see `ScalarConfig` for all available fields.
368
+ *
369
+ * @example
370
+ * scalar: { theme: 'purple', darkMode: true, layout: 'classic' }
371
+ */
372
+ scalar?: ScalarConfig;
373
+ }
268
374
  interface CrudoraServerConfig {
269
375
  port?: number;
270
376
  /**
@@ -297,20 +403,66 @@ interface CrudoraServerConfig {
297
403
  * - Omit / `undefined` (default): enabled with 100 requests per minute per IP.
298
404
  * - Pass a `RateLimitConfig` object to customise the window, limit, or key function.
299
405
  * - Pass `false` to disable rate limiting entirely (e.g. when using an external proxy-level limiter).
406
+ *
407
+ * **Important:** This limiter is in-memory and per-process. In multi-instance deployments
408
+ * (load balancer, Kubernetes replicas), each instance tracks its own counter independently —
409
+ * the effective limit per client is `max × instanceCount`. Use a Redis-backed limiter
410
+ * (e.g. `rate-limiter-flexible`) and pass `rateLimit: false` to disable this one.
300
411
  */
301
412
  rateLimit?: RateLimitConfig | false;
413
+ /**
414
+ * Socket-level request timeout in milliseconds.
415
+ * Requests that exceed this duration are terminated with a `503` response.
416
+ * - Omit / `0` (default): no timeout.
417
+ * - Recommended: `30_000` (30 s) for most APIs.
418
+ */
419
+ timeout?: number;
420
+ /**
421
+ * Built-in health check endpoint.
422
+ * - `true` (default): mounts `GET /health` returning `{ status: 'ok' }`.
423
+ * - `string`: custom path, e.g. `'/healthz'`.
424
+ * - `false`: disable entirely.
425
+ */
426
+ healthCheck?: boolean | string;
427
+ /**
428
+ * Built-in API documentation powered by Scalar.
429
+ * Requires `@scalar/express-api-reference` to be installed separately.
430
+ *
431
+ * - `false` (default): disabled.
432
+ * - `true`: mount UI at `GET /docs` and spec at `GET /docs/openapi.json`.
433
+ * - `string`: custom base path, e.g. `'/api-docs'`.
434
+ * - `DocsConfig`: full control — path, OpenAPI info, Scalar theme/layout/etc.
435
+ *
436
+ * @example
437
+ * docs: { path: '/docs', title: 'My API', scalar: { theme: 'purple' } }
438
+ */
439
+ docs?: boolean | string | DocsConfig;
302
440
  }
303
441
  declare class CrudoraServer {
304
442
  private app;
305
443
  private crudora;
306
444
  private config;
445
+ private httpServer;
307
446
  constructor(config: CrudoraServerConfig);
308
447
  private setupMiddleware;
309
448
  registerModel(...modelClasses: ModelConstructor[]): this;
310
449
  registerTable<T extends Model>(modelClass: ModelConstructor<T>, table: any): this;
311
450
  generateRoutes(): this;
312
451
  use(middleware: any): this;
313
- listen(callback?: () => void): void;
452
+ /**
453
+ * Starts the HTTP server and returns the underlying `http.Server` instance.
454
+ * Use the returned server for graceful shutdown:
455
+ *
456
+ * @example
457
+ * const httpServer = server.listen();
458
+ * process.on('SIGTERM', () => httpServer.close(() => process.exit(0)));
459
+ */
460
+ listen(callback?: () => void): http.Server;
461
+ /**
462
+ * Returns the `http.Server` instance after `listen()` has been called, or `null` before.
463
+ * Useful when you need the server reference without calling listen again.
464
+ */
465
+ getHttpServer(): http.Server | null;
314
466
  getApp(): Express;
315
467
  getCrudora(): Crudora;
316
468
  /**
@@ -401,4 +553,4 @@ declare function BelongsTo(model: () => any, foreignKey: string, ownerKey?: stri
401
553
  declare function BelongsToMany(model: () => any, pivotModel: () => any, pivotForeignKey: string, pivotRelatedKey: string, localKey?: string): (target: any, propertyKey: string) => void;
402
554
  declare function getRelationMetadata(target: any): Record<string, RelationDefinition>;
403
555
 
404
- export { BelongsTo, BelongsToMany, Crudora, type CrudoraLogger, CrudoraServer, type CrudoraServerConfig, type CursorPaginationOptions, type CursorResult, type Dialect, DrizzleTableBuilder, Field, type FieldOptions, type FieldType, type FindAllOptions, type FindByIdOptions, HasMany, HasOne, Model, type ModelConstructor, type ModelOptions, NotFoundError, type RateLimitConfig, type RelationDefinition, type RelationType, Repository, SchemaGenerator, ValidationGenerator, getFieldMetadata, getRelationMetadata };
556
+ export { BelongsTo, BelongsToMany, Crudora, type CrudoraLogger, CrudoraServer, type CrudoraServerConfig, type CursorPaginationOptions, type CursorResult, type Dialect, type DocsConfig, DrizzleTableBuilder, Field, type FieldOptions, type FieldType, type FindAllOptions, type FindByIdOptions, HasMany, HasOne, Model, type ModelConstructor, type ModelOptions, NotFoundError, OpenApiGenerator, type OpenApiInfo, type RateLimitConfig, type RelationDefinition, type RelationType, Repository, type ScalarConfig, SchemaGenerator, ValidationGenerator, getFieldMetadata, getRelationMetadata };