@classytic/arc 1.0.0 → 1.0.8

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.
Files changed (36) hide show
  1. package/README.md +65 -35
  2. package/bin/arc.js +118 -103
  3. package/dist/BaseController-nNRS3vpA.d.ts +233 -0
  4. package/dist/adapters/index.d.ts +2 -2
  5. package/dist/{arcCorePlugin-DTPWXcZN.d.ts → arcCorePlugin-CAjBQtZB.d.ts} +1 -1
  6. package/dist/auth/index.d.ts +1 -1
  7. package/dist/cli/commands/generate.d.ts +16 -0
  8. package/dist/cli/commands/generate.js +334 -0
  9. package/dist/cli/commands/init.d.ts +24 -0
  10. package/dist/cli/commands/init.js +2425 -0
  11. package/dist/cli/index.d.ts +4 -43
  12. package/dist/cli/index.js +3160 -411
  13. package/dist/core/index.d.ts +220 -0
  14. package/dist/core/index.js +2764 -0
  15. package/dist/{createApp-pzUAkzbz.d.ts → createApp-CjN9zZSL.d.ts} +1 -1
  16. package/dist/docs/index.js +19 -11
  17. package/dist/factory/index.d.ts +4 -4
  18. package/dist/factory/index.js +6 -23
  19. package/dist/hooks/index.d.ts +1 -1
  20. package/dist/{index-DkAW8BXh.d.ts → index-D5QTob1X.d.ts} +32 -12
  21. package/dist/index.d.ts +7 -203
  22. package/dist/index.js +108 -113
  23. package/dist/org/index.d.ts +1 -1
  24. package/dist/permissions/index.js +5 -2
  25. package/dist/plugins/index.d.ts +2 -2
  26. package/dist/presets/index.d.ts +6 -6
  27. package/dist/presets/index.js +3 -1
  28. package/dist/presets/multiTenant.d.ts +1 -1
  29. package/dist/registry/index.d.ts +2 -2
  30. package/dist/testing/index.d.ts +2 -2
  31. package/dist/testing/index.js +6 -23
  32. package/dist/types/index.d.ts +1 -1
  33. package/dist/{types-0IPhH_NR.d.ts → types-zpN48n6B.d.ts} +1 -1
  34. package/dist/utils/index.d.ts +28 -4
  35. package/dist/utils/index.js +17 -8
  36. package/package.json +8 -14
@@ -1,5 +1,5 @@
1
1
  import { FastifyInstance } from 'fastify';
2
- import { C as CreateAppOptions } from './types-0IPhH_NR.js';
2
+ import { C as CreateAppOptions } from './types-zpN48n6B.js';
3
3
 
4
4
  /**
5
5
  * ArcFactory - Production-ready Fastify application factory
@@ -538,11 +538,16 @@ function generateSchemas(resources) {
538
538
  };
539
539
  for (const resource of resources) {
540
540
  const storedSchemas = resource.openApiSchemas;
541
- if (storedSchemas?.createBody) {
541
+ if (storedSchemas?.response) {
542
+ schemas[resource.name] = {
543
+ type: "object",
544
+ description: resource.displayName,
545
+ ...storedSchemas.response
546
+ };
547
+ } else if (storedSchemas?.createBody) {
542
548
  schemas[resource.name] = {
543
549
  type: "object",
544
550
  description: resource.displayName,
545
- ...storedSchemas.createBody,
546
551
  properties: {
547
552
  _id: { type: "string", description: "Unique identifier" },
548
553
  ...storedSchemas.createBody.properties ?? {},
@@ -550,6 +555,18 @@ function generateSchemas(resources) {
550
555
  updatedAt: { type: "string", format: "date-time", description: "Last update timestamp" }
551
556
  }
552
557
  };
558
+ } else {
559
+ schemas[resource.name] = {
560
+ type: "object",
561
+ description: resource.displayName,
562
+ properties: {
563
+ _id: { type: "string", description: "Unique identifier" },
564
+ createdAt: { type: "string", format: "date-time", description: "Creation timestamp" },
565
+ updatedAt: { type: "string", format: "date-time", description: "Last update timestamp" }
566
+ }
567
+ };
568
+ }
569
+ if (storedSchemas?.createBody) {
553
570
  schemas[`${resource.name}Input`] = {
554
571
  type: "object",
555
572
  description: `${resource.displayName} create input`,
@@ -563,15 +580,6 @@ function generateSchemas(resources) {
563
580
  };
564
581
  }
565
582
  } else {
566
- schemas[resource.name] = {
567
- type: "object",
568
- description: resource.displayName,
569
- properties: {
570
- _id: { type: "string", description: "Unique identifier" },
571
- createdAt: { type: "string", format: "date-time", description: "Creation timestamp" },
572
- updatedAt: { type: "string", format: "date-time", description: "Last update timestamp" }
573
- }
574
- };
575
583
  schemas[`${resource.name}Input`] = {
576
584
  type: "object",
577
585
  description: `${resource.displayName} input`
@@ -1,11 +1,11 @@
1
- export { A as ArcFactory, c as createApp } from '../createApp-pzUAkzbz.js';
2
- import { C as CreateAppOptions } from '../types-0IPhH_NR.js';
3
- export { M as MultipartOptions, R as RawBodyOptions, U as UnderPressureOptions } from '../types-0IPhH_NR.js';
1
+ export { A as ArcFactory, c as createApp } from '../createApp-CjN9zZSL.js';
2
+ import { C as CreateAppOptions } from '../types-zpN48n6B.js';
3
+ export { M as MultipartOptions, R as RawBodyOptions, U as UnderPressureOptions } from '../types-zpN48n6B.js';
4
4
  import 'fastify';
5
5
  import '@fastify/cors';
6
6
  import '@fastify/helmet';
7
7
  import '@fastify/rate-limit';
8
- import '../index-DkAW8BXh.js';
8
+ import '../index-D5QTob1X.js';
9
9
  import 'mongoose';
10
10
  import '../types-B99TBmFV.js';
11
11
 
@@ -1478,9 +1478,7 @@ async function loadPlugin(name, logger) {
1478
1478
  const err = error;
1479
1479
  const isModuleNotFound = err.message.includes("Cannot find module") || err.message.includes("Cannot find package") || err.message.includes("MODULE_NOT_FOUND") || err.message.includes("Could not resolve");
1480
1480
  if (isModuleNotFound && OPTIONAL_PLUGINS.has(name)) {
1481
- logger?.warn(
1482
- `ℹ️ Optional plugin '${name}' skipped (${packageName} not installed)`
1483
- );
1481
+ logger?.warn(`ℹ️ Optional plugin '${name}' skipped (${packageName} not installed)`);
1484
1482
  return null;
1485
1483
  }
1486
1484
  if (isModuleNotFound) {
@@ -1519,10 +1517,7 @@ async function createApp(options) {
1519
1517
  });
1520
1518
  if (config.helmet !== false) {
1521
1519
  const helmet = await loadPlugin("helmet");
1522
- await fastify.register(
1523
- helmet,
1524
- config.helmet ?? {}
1525
- );
1520
+ await fastify.register(helmet, config.helmet ?? {});
1526
1521
  fastify.log.info("✅ Helmet (security headers) enabled");
1527
1522
  } else {
1528
1523
  fastify.log.warn("⚠️ Helmet disabled - security headers not applied");
@@ -1542,20 +1537,14 @@ async function createApp(options) {
1542
1537
  }
1543
1538
  if (config.rateLimit !== false) {
1544
1539
  const rateLimit = await loadPlugin("rateLimit");
1545
- await fastify.register(
1546
- rateLimit,
1547
- config.rateLimit ?? { max: 100, timeWindow: "1 minute" }
1548
- );
1540
+ await fastify.register(rateLimit, config.rateLimit ?? { max: 100, timeWindow: "1 minute" });
1549
1541
  fastify.log.info("✅ Rate limiting enabled");
1550
1542
  } else {
1551
1543
  fastify.log.warn("⚠️ Rate limiting disabled");
1552
1544
  }
1553
1545
  if (config.underPressure !== false) {
1554
1546
  const underPressure = await loadPlugin("underPressure");
1555
- await fastify.register(
1556
- underPressure,
1557
- config.underPressure ?? { exposeStatusRoute: true }
1558
- );
1547
+ await fastify.register(underPressure, config.underPressure ?? { exposeStatusRoute: true });
1559
1548
  fastify.log.info("✅ Health monitoring (under-pressure) enabled");
1560
1549
  } else {
1561
1550
  fastify.log.info("ℹ️ Health monitoring disabled");
@@ -1575,10 +1564,7 @@ async function createApp(options) {
1575
1564
  files: 10
1576
1565
  }
1577
1566
  };
1578
- await fastify.register(multipart, {
1579
- ...multipartDefaults,
1580
- ...config.multipart
1581
- });
1567
+ await fastify.register(multipart, { ...multipartDefaults, ...config.multipart });
1582
1568
  fastify.log.info("✅ Multipart (file uploads) enabled");
1583
1569
  }
1584
1570
  }
@@ -1591,10 +1577,7 @@ async function createApp(options) {
1591
1577
  encoding: "utf8",
1592
1578
  runFirst: true
1593
1579
  };
1594
- await fastify.register(rawBody, {
1595
- ...rawBodyDefaults,
1596
- ...config.rawBody
1597
- });
1580
+ await fastify.register(rawBody, { ...rawBodyDefaults, ...config.rawBody });
1598
1581
  fastify.log.info("✅ Raw body parsing enabled");
1599
1582
  }
1600
1583
  }
@@ -1,4 +1,4 @@
1
- export { ac as HookContext, ad as HookHandler, aH as HookOperation, aG as HookPhase, aI as HookRegistration, H as HookSystem, aJ as HookSystemOptions, aB as afterCreate, aF as afterDelete, aD as afterUpdate, aA as beforeCreate, aE as beforeDelete, aC as beforeUpdate, az as createHookSystem, ab as hookSystem } from '../index-DkAW8BXh.js';
1
+ export { ac as HookContext, ad as HookHandler, aH as HookOperation, aG as HookPhase, aI as HookRegistration, H as HookSystem, aJ as HookSystemOptions, aB as afterCreate, aF as afterDelete, aD as afterUpdate, aA as beforeCreate, aE as beforeDelete, aC as beforeUpdate, az as createHookSystem, ab as hookSystem } from '../index-D5QTob1X.js';
2
2
  import 'mongoose';
3
3
  import 'fastify';
4
4
  import '../types-B99TBmFV.js';
@@ -405,8 +405,8 @@ interface IRequestContext {
405
405
  headers: Record<string, string | undefined>;
406
406
  /** Organization ID (for multi-tenant apps) */
407
407
  organizationId?: string;
408
- /** Additional context data */
409
- context?: Record<string, unknown>;
408
+ /** Additional metadata and custom fields */
409
+ metadata?: Record<string, unknown>;
410
410
  }
411
411
  /**
412
412
  * Standard response from controller handlers
@@ -428,13 +428,13 @@ interface IControllerResponse<T = unknown> {
428
428
  /**
429
429
  * Controller handler - Arc's standard pattern
430
430
  *
431
- * Receives a context object, returns IControllerResponse.
431
+ * Receives a request context object, returns IControllerResponse.
432
432
  * Use with `wrapHandler: true` in additionalRoutes.
433
433
  *
434
434
  * @example
435
435
  * ```typescript
436
- * const createProduct: ControllerHandler<Product> = async (ctx) => {
437
- * const product = await productRepo.create(ctx.body);
436
+ * const createProduct: ControllerHandler<Product> = async (req) => {
437
+ * const product = await productRepo.create(req.body);
438
438
  * return { success: true, data: product, status: 201 };
439
439
  * };
440
440
  *
@@ -447,7 +447,7 @@ interface IControllerResponse<T = unknown> {
447
447
  * }]
448
448
  * ```
449
449
  */
450
- type ControllerHandler<T = unknown> = (context: IRequestContext) => Promise<IControllerResponse<T>>;
450
+ type ControllerHandler<T = unknown> = (req: IRequestContext) => Promise<IControllerResponse<T>>;
451
451
  /**
452
452
  * Fastify native handler
453
453
  *
@@ -480,14 +480,14 @@ type RouteHandler = ControllerHandler | FastifyHandler;
480
480
  * Controller interface for CRUD operations (strict)
481
481
  */
482
482
  interface IController<TDoc = unknown> {
483
- list(context: IRequestContext): Promise<IControllerResponse<{
483
+ list(req: IRequestContext): Promise<IControllerResponse<{
484
484
  docs: TDoc[];
485
485
  total: number;
486
486
  }>>;
487
- get(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
488
- create(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
489
- update(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
490
- delete(context: IRequestContext): Promise<IControllerResponse<{
487
+ get(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
488
+ create(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
489
+ update(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
490
+ delete(req: IRequestContext): Promise<IControllerResponse<{
491
491
  message: string;
492
492
  }>>;
493
493
  }
@@ -832,6 +832,26 @@ interface OpenApiSchemas {
832
832
  updateBody?: unknown;
833
833
  params?: unknown;
834
834
  listQuery?: unknown;
835
+ /**
836
+ * Explicit response schema for OpenAPI documentation.
837
+ * If provided, this will be used as-is for the response schema.
838
+ * If not provided, response schema is auto-generated from createBody.
839
+ *
840
+ * Note: This is for OpenAPI docs only - does NOT affect Fastify serialization.
841
+ *
842
+ * @example
843
+ * response: {
844
+ * type: 'object',
845
+ * properties: {
846
+ * _id: { type: 'string' },
847
+ * name: { type: 'string' },
848
+ * email: { type: 'string' },
849
+ * // Exclude password, include virtuals
850
+ * fullName: { type: 'string' },
851
+ * }
852
+ * }
853
+ */
854
+ response?: unknown;
835
855
  [key: string]: unknown;
836
856
  }
837
857
  /** Handler for middleware functions */
@@ -1299,4 +1319,4 @@ interface ValidationResult {
1299
1319
  }
1300
1320
  type AdapterFactory<TDoc> = (config: unknown) => DataAdapter<TDoc>;
1301
1321
 
1302
- export { type InferDocType as $, type AuthHelpers as A, type CrudController as B, type CrudRouteKey as C, type DataAdapter as D, type FieldRule as E, type FieldMetadata as F, type CrudSchemas as G, HookSystem as H, type IntrospectionPluginOptions as I, type JWTPayload as J, type AdditionalRoute as K, type PresetFunction as L, type MiddlewareConfig as M, type EventDefinition as N, type OwnershipCheck as O, type PresetResult as P, type QueryOptions as Q, type RequestWithExtras as R, type ServiceContext as S, type ResourceMetadata as T, type UserOrganization as U, type ValidationResult as V, type RegistryEntry as W, type RegistryStats as X, type IntrospectionData as Y, type OrgScopeOptions as Z, type CrudRouterOptions as _, type AuthPluginOptions as a, type InferResourceDoc as a0, type TypedResourceConfig as a1, type TypedController as a2, type TypedRepository as a3, type ConfigError as a4, type ValidationResult$1 as a5, type ValidateOptions as a6, type HealthCheck as a7, type HealthOptions as a8, type GracefulShutdownOptions as a9, beforeCreate as aA, afterCreate as aB, beforeUpdate as aC, afterUpdate as aD, beforeDelete as aE, afterDelete as aF, type HookPhase as aG, type HookOperation as aH, type HookRegistration as aI, type HookSystemOptions as aJ, type RequestIdOptions as aa, hookSystem as ab, type HookContext as ac, type HookHandler as ad, type ParsedQuery as ae, type OpenApiSchemas as af, type AdapterFactory as ag, type ObjectId as ah, type UserLike as ai, getUserId as aj, type ArcDecorator as ak, type EventsDecorator as al, type ResourcePermissions as am, type ResourceHooks as an, type MiddlewareHandler as ao, type JwtContext as ap, type AuthenticatorContext as aq, type Authenticator as ar, type TokenPair as as, type PresetHook as at, type BaseControllerOptions as au, type PaginationParams as av, type InferDoc as aw, type ControllerHandler as ax, type FastifyHandler as ay, createHookSystem as az, ResourceRegistry as b, type RegisterOptions as c, type AnyRecord as d, type IController as e, type CrudRepository as f, type RouteSchemaOptions as g, type QueryParserInterface as h, type RepositoryLike as i, type IRequestContext as j, type ControllerQueryOptions as k, type RequestContext as l, type IControllerResponse as m, type PaginatedResult as n, type ResourceConfig as o, type SchemaMetadata as p, type RelationMetadata as q, resourceRegistry as r, defineResource as s, ResourceDefinition as t, type ApiResponse as u, type ControllerLike as v, type FastifyRequestExtras as w, type FastifyWithAuth as x, type FastifyWithDecorators as y, type RouteHandler as z };
1322
+ export { type InferDocType as $, type AnyRecord as A, type CrudController as B, type ControllerQueryOptions as C, type DataAdapter as D, type FieldRule as E, type FieldMetadata as F, type CrudSchemas as G, HookSystem as H, type IController as I, type JWTPayload as J, type AdditionalRoute as K, type PresetFunction as L, type MiddlewareConfig as M, type EventDefinition as N, type OwnershipCheck as O, type PaginatedResult as P, type QueryParserInterface as Q, type RouteSchemaOptions as R, type ServiceContext as S, type ResourceMetadata as T, type UserOrganization as U, type ValidationResult as V, type RegistryEntry as W, type RegistryStats as X, type IntrospectionData as Y, type OrgScopeOptions as Z, type CrudRouterOptions as _, type IRequestContext as a, type InferResourceDoc as a0, type TypedResourceConfig as a1, type TypedController as a2, type TypedRepository as a3, type ConfigError as a4, type ValidationResult$1 as a5, type ValidateOptions as a6, type HealthCheck as a7, type HealthOptions as a8, type GracefulShutdownOptions as a9, beforeCreate as aA, afterCreate as aB, beforeUpdate as aC, afterUpdate as aD, beforeDelete as aE, afterDelete as aF, type HookPhase as aG, type HookOperation as aH, type HookRegistration as aI, type HookSystemOptions as aJ, type RequestIdOptions as aa, hookSystem as ab, type HookContext as ac, type HookHandler as ad, type ParsedQuery as ae, type OpenApiSchemas as af, type AdapterFactory as ag, type ObjectId as ah, type UserLike as ai, getUserId as aj, type ArcDecorator as ak, type EventsDecorator as al, type ResourcePermissions as am, type ResourceHooks as an, type MiddlewareHandler as ao, type JwtContext as ap, type AuthenticatorContext as aq, type Authenticator as ar, type TokenPair as as, type PresetHook as at, type BaseControllerOptions as au, type PaginationParams as av, type InferDoc as aw, type ControllerHandler as ax, type FastifyHandler as ay, createHookSystem as az, type RequestContext as b, type IControllerResponse as c, type RequestWithExtras as d, type CrudRouteKey as e, type PresetResult as f, type AuthHelpers as g, type AuthPluginOptions as h, type IntrospectionPluginOptions as i, ResourceRegistry as j, type RegisterOptions as k, type ResourceConfig as l, type SchemaMetadata as m, type RelationMetadata as n, type RepositoryLike as o, defineResource as p, ResourceDefinition as q, resourceRegistry as r, type ApiResponse as s, type ControllerLike as t, type FastifyRequestExtras as u, type FastifyWithAuth as v, type FastifyWithDecorators as w, type QueryOptions as x, type CrudRepository as y, type RouteHandler as z };
package/dist/index.d.ts CHANGED
@@ -1,216 +1,20 @@
1
- import { d as AnyRecord, e as IController, f as CrudRepository, Q as QueryOptions, g as RouteSchemaOptions, h as QueryParserInterface, i as RepositoryLike, j as IRequestContext, S as ServiceContext, k as ControllerQueryOptions, l as RequestContext, H as HookSystem, m as IControllerResponse, n as PaginatedResult, o as ResourceConfig } from './index-DkAW8BXh.js';
2
- export { V as AdapterValidationResult, K as AdditionalRoute, u as ApiResponse, a as AuthPluginOptions, a4 as ConfigError, v as ControllerLike, B as CrudController, C as CrudRouteKey, _ as CrudRouterOptions, G as CrudSchemas, D as DataAdapter, N as EventDefinition, w as FastifyRequestExtras, x as FastifyWithAuth, y as FastifyWithDecorators, F as FieldMetadata, E as FieldRule, a9 as GracefulShutdownOptions, a7 as HealthCheck, a8 as HealthOptions, ac as HookContext, ad as HookHandler, $ as InferDocType, a0 as InferResourceDoc, Y as IntrospectionData, I as IntrospectionPluginOptions, J as JWTPayload, M as MiddlewareConfig, Z as OrgScopeOptions, O as OwnershipCheck, L as PresetFunction, P as PresetResult, W as RegistryEntry, X as RegistryStats, q as RelationMetadata, aa as RequestIdOptions, R as RequestWithExtras, t as ResourceDefinition, T as ResourceMetadata, z as RouteHandler, p as SchemaMetadata, a2 as TypedController, a3 as TypedRepository, a1 as TypedResourceConfig, U as UserOrganization, a6 as ValidateOptions, a5 as ValidationResult, s as defineResource, ab as hookSystem, r as resourceRegistry } from './index-DkAW8BXh.js';
1
+ import { l as ResourceConfig } from './index-D5QTob1X.js';
2
+ export { V as AdapterValidationResult, K as AdditionalRoute, A as AnyRecord, s as ApiResponse, h as AuthPluginOptions, a4 as ConfigError, t as ControllerLike, B as CrudController, y as CrudRepository, e as CrudRouteKey, _ as CrudRouterOptions, G as CrudSchemas, D as DataAdapter, N as EventDefinition, u as FastifyRequestExtras, v as FastifyWithAuth, w as FastifyWithDecorators, F as FieldMetadata, E as FieldRule, a9 as GracefulShutdownOptions, a7 as HealthCheck, a8 as HealthOptions, ac as HookContext, ad as HookHandler, H as HookSystem, I as IController, c as IControllerResponse, a as IRequestContext, $ as InferDocType, a0 as InferResourceDoc, Y as IntrospectionData, i as IntrospectionPluginOptions, J as JWTPayload, M as MiddlewareConfig, Z as OrgScopeOptions, O as OwnershipCheck, P as PaginatedResult, L as PresetFunction, f as PresetResult, x as QueryOptions, W as RegistryEntry, X as RegistryStats, n as RelationMetadata, o as RepositoryLike, b as RequestContext, aa as RequestIdOptions, d as RequestWithExtras, q as ResourceDefinition, T as ResourceMetadata, z as RouteHandler, R as RouteSchemaOptions, m as SchemaMetadata, S as ServiceContext, a2 as TypedController, a3 as TypedRepository, a1 as TypedResourceConfig, U as UserOrganization, a6 as ValidateOptions, a5 as ValidationResult, p as defineResource, ab as hookSystem, r as resourceRegistry } from './index-D5QTob1X.js';
3
3
  export { MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, createMongooseAdapter, createPrismaAdapter } from './adapters/index.js';
4
+ export { B as BaseController, a as BaseControllerOptions } from './BaseController-nNRS3vpA.js';
4
5
  export { RouteHandlerMethod } from 'fastify';
5
6
  export { P as PermissionCheck, a as PermissionContext, b as PermissionResult, U as UserBase } from './types-B99TBmFV.js';
6
7
  export { A as ArcError, F as ForbiddenError, N as NotFoundError, U as UnauthorizedError, V as ValidationError } from './errors-8WIxGS_6.js';
7
- export { e as gracefulShutdownPlugin, a as healthPlugin, _ as requestIdPlugin } from './arcCorePlugin-DTPWXcZN.js';
8
+ export { e as gracefulShutdownPlugin, a as healthPlugin, _ as requestIdPlugin } from './arcCorePlugin-CAjBQtZB.js';
8
9
  export { DomainEvent, EventHandler, eventPlugin } from './events/index.js';
9
10
  export { allOf, allowPublic, anyOf, denyAll, requireAuth, requireOwnership, requireRoles, when } from './permissions/index.js';
10
- export { A as ArcFactory, c as createApp } from './createApp-pzUAkzbz.js';
11
- export { C as CreateAppOptions } from './types-0IPhH_NR.js';
11
+ export { A as ArcFactory, c as createApp } from './createApp-CjN9zZSL.js';
12
+ export { C as CreateAppOptions } from './types-zpN48n6B.js';
12
13
  import 'mongoose';
13
14
  import '@fastify/cors';
14
15
  import '@fastify/helmet';
15
16
  import '@fastify/rate-limit';
16
17
 
17
- /**
18
- * Base Controller - Framework-Agnostic CRUD Operations
19
- *
20
- * Implements IController interface for framework portability.
21
- * Works with Fastify, Express, Next.js, or any framework via adapter pattern.
22
- *
23
- * @example
24
- * import { BaseController } from '@classytic/arc';
25
- *
26
- * // Use Arc's default query parser (works out of the box)
27
- * class ProductController extends BaseController {
28
- * constructor(repository: CrudRepository) {
29
- * super(repository);
30
- * }
31
- * }
32
- *
33
- * // Or use MongoKit's parser for advanced MongoDB features ($lookup, aggregations)
34
- * import { QueryParser } from '@classytic/mongokit';
35
- * defineResource({
36
- * name: 'product',
37
- * queryParser: new QueryParser(),
38
- * // ...
39
- * });
40
- *
41
- * // Or use a custom parser for SQL databases
42
- * defineResource({
43
- * name: 'user',
44
- * queryParser: new PgQueryParser(),
45
- * // ...
46
- * });
47
- */
48
-
49
- /**
50
- * Extended repository type that includes optional preset methods
51
- * Used internally for type-safe access to preset-added methods
52
- */
53
- interface ExtendedRepository<TDoc> extends CrudRepository<TDoc> {
54
- getBySlug?(slug: string, options?: QueryOptions): Promise<TDoc | null>;
55
- getDeleted?(options?: QueryOptions): Promise<TDoc[]>;
56
- restore?(id: string, options?: QueryOptions): Promise<TDoc | null>;
57
- getTree?(options?: QueryOptions): Promise<TDoc[]>;
58
- getChildren?(parentId: string, options?: QueryOptions): Promise<TDoc[]>;
59
- }
60
- interface BaseControllerOptions {
61
- /** Schema options for field sanitization */
62
- schemaOptions?: RouteSchemaOptions;
63
- /**
64
- * Query parser instance
65
- * Default: Arc built-in query parser (adapter-agnostic).
66
- * You can swap in MongoKit QueryParser, pgkit parser, etc.
67
- */
68
- queryParser?: QueryParserInterface;
69
- /** Maximum limit for pagination (default: 100) */
70
- maxLimit?: number;
71
- /** Default limit for pagination (default: 20) */
72
- defaultLimit?: number;
73
- /** Default sort field (default: '-createdAt') */
74
- defaultSort?: string;
75
- /** Resource name for hook execution (e.g., 'product' → 'product.created') */
76
- resourceName?: string;
77
- /** Disable automatic event emission (default: false) */
78
- disableEvents?: boolean;
79
- }
80
- /**
81
- * Framework-agnostic base controller implementing MongoKit's IController
82
- *
83
- * Use with Fastify adapter for Fastify integration (see createFastifyAdapter in createCrudRouter)
84
- */
85
- declare class BaseController<TDoc = AnyRecord> implements IController<TDoc> {
86
- protected repository: ExtendedRepository<TDoc>;
87
- protected schemaOptions: RouteSchemaOptions;
88
- protected queryParser: QueryParserInterface;
89
- protected maxLimit: number;
90
- protected defaultLimit: number;
91
- protected defaultSort: string;
92
- protected resourceName?: string;
93
- protected disableEvents: boolean;
94
- /** Preset field names for dynamic param reading */
95
- protected _presetFields: {
96
- slugField?: string;
97
- parentField?: string;
98
- };
99
- constructor(repository: CrudRepository<TDoc> | RepositoryLike, options?: BaseControllerOptions);
100
- /**
101
- * Inject resource options from defineResource
102
- */
103
- _setResourceOptions(options: {
104
- schemaOptions?: RouteSchemaOptions;
105
- presetFields?: {
106
- slugField?: string;
107
- parentField?: string;
108
- };
109
- resourceName?: string;
110
- queryParser?: QueryParserInterface;
111
- }): void;
112
- /**
113
- * Build service context from IRequestContext
114
- */
115
- protected _buildContext(context: IRequestContext): ServiceContext;
116
- /**
117
- * Parse query into QueryOptions using queryParser
118
- */
119
- protected _parseQueryOptions(context: IRequestContext): ControllerQueryOptions;
120
- /**
121
- * Apply org and policy filters
122
- */
123
- protected _applyFilters(options: ControllerQueryOptions, context: IRequestContext): ControllerQueryOptions;
124
- /**
125
- * Build filter for single-item operations (get/update/delete)
126
- * Combines ID filter with policy/org filters for proper security enforcement
127
- */
128
- protected _buildIdFilter(id: string, context: IRequestContext): AnyRecord;
129
- /**
130
- * Check if a value matches a MongoDB query operator
131
- */
132
- protected _matchesOperator(itemValue: unknown, operator: string, filterValue: unknown): boolean;
133
- /**
134
- * Forbidden paths that could lead to prototype pollution
135
- */
136
- private static readonly FORBIDDEN_PATHS;
137
- /**
138
- * Get nested value from object using dot notation (e.g., "owner.id")
139
- * Security: Validates path against forbidden patterns to prevent prototype pollution
140
- */
141
- protected _getNestedValue(obj: AnyRecord, path: string): unknown;
142
- /**
143
- * Check if item matches a single filter condition
144
- * Supports nested paths (e.g., "owner.id", "metadata.status")
145
- */
146
- protected _matchesFilter(item: AnyRecord, key: string, filterValue: unknown): boolean;
147
- /**
148
- * Check if item matches policy filters (for get/update/delete operations)
149
- * Validates that fetched item satisfies all policy constraints
150
- * Supports MongoDB query operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex, $and, $or
151
- */
152
- protected _checkPolicyFilters(item: AnyRecord, context: IRequestContext): boolean;
153
- /** Parse lean option (default: true for performance) */
154
- protected _parseLean(leanValue: unknown): boolean;
155
- /** Get blocked fields from schema options */
156
- protected _getBlockedFields(schemaOptions: RouteSchemaOptions): string[];
157
- /**
158
- * Convert parsed select object to string format
159
- * Converts { name: 1, email: 1, password: 0 } → 'name email -password'
160
- */
161
- protected _selectToString(select: string | string[] | Record<string, 0 | 1> | undefined): string | undefined;
162
- /** Sanitize select fields */
163
- protected _sanitizeSelect(select: string | undefined, schemaOptions: RouteSchemaOptions): string | undefined;
164
- /** Sanitize populate fields */
165
- protected _sanitizePopulate(populate: unknown, schemaOptions: RouteSchemaOptions): string[] | undefined;
166
- /** Check org scope for a document */
167
- protected _checkOrgScope(item: AnyRecord | null, arcContext: RequestContext | undefined): boolean;
168
- /** Check ownership for update/delete (ownedByUser preset) */
169
- protected _checkOwnership(item: AnyRecord | null, context: IRequestContext): boolean;
170
- /**
171
- * Get hook system from context (instance-scoped) or fall back to global singleton
172
- * This allows proper isolation when running multiple app instances (e.g., in tests)
173
- */
174
- protected _getHooks(context: IRequestContext): HookSystem;
175
- /**
176
- * List resources with filtering, pagination, sorting
177
- * Implements IController.list()
178
- */
179
- list(context: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
180
- /**
181
- * Get single resource by ID
182
- * Implements IController.get()
183
- */
184
- get(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
185
- /**
186
- * Create new resource
187
- * Implements IController.create()
188
- */
189
- create(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
190
- /**
191
- * Update existing resource
192
- * Implements IController.update()
193
- */
194
- update(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
195
- /**
196
- * Delete resource
197
- * Implements IController.delete()
198
- */
199
- delete(context: IRequestContext): Promise<IControllerResponse<{
200
- message: string;
201
- }>>;
202
- /** Get resource by slug (slugLookup preset) */
203
- getBySlug(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
204
- /** Get soft-deleted resources (softDelete preset) */
205
- getDeleted(context: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
206
- /** Restore soft-deleted resource (softDelete preset) */
207
- restore(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
208
- /** Get hierarchical tree (tree preset) */
209
- getTree(context: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
210
- /** Get children of parent (tree preset) */
211
- getChildren(context: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
212
- }
213
-
214
18
  /**
215
19
  * Resource Configuration Validator
216
20
  *
@@ -328,4 +132,4 @@ declare function assertValidConfig(config: ResourceConfig, options?: ValidateOpt
328
132
 
329
133
  declare const version = "1.0.0";
330
134
 
331
- export { AnyRecord, BaseController, type BaseControllerOptions, CrudRepository, HookSystem, IController, IControllerResponse, IRequestContext, PaginatedResult, QueryOptions, RepositoryLike, RequestContext, ResourceConfig, RouteSchemaOptions, ServiceContext, assertValidConfig, formatValidationErrors, validateResourceConfig, version };
135
+ export { ResourceConfig, assertValidConfig, formatValidationErrors, validateResourceConfig, version };