@classytic/arc 2.4.3 → 2.5.1

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 (58) hide show
  1. package/dist/{BaseController-CkM5dUh_.mjs → BaseController-CNwMYpDW.mjs} +1 -1
  2. package/dist/adapters/index.d.mts +2 -2
  3. package/dist/auth/index.d.mts +1 -1
  4. package/dist/auth/index.mjs +2 -2
  5. package/dist/core/index.d.mts +2 -2
  6. package/dist/core/index.mjs +2 -2
  7. package/dist/{createApp-CBgVaFyh.mjs → createApp-oic3-ieX.mjs} +3 -3
  8. package/dist/{defineResource-B22gcNvn.mjs → defineResource-BYm3CIoe.mjs} +85 -10
  9. package/dist/docs/index.d.mts +1 -1
  10. package/dist/dynamic/index.d.mts +1 -1
  11. package/dist/dynamic/index.mjs +2 -2
  12. package/dist/{elevation-Ca_yveIO.d.mts → elevation-C_taLQrM.d.mts} +27 -1
  13. package/dist/{errorHandler-DMbGdzBG.mjs → errorHandler-r2595m8T.mjs} +1 -1
  14. package/dist/{errors-CPpvPHT0.d.mts → errors-CcVbl1-T.d.mts} +17 -1
  15. package/dist/{errors-rxhfP7Hf.mjs → errors-NoQKsbAT.mjs} +23 -1
  16. package/dist/factory/index.d.mts +1 -1
  17. package/dist/factory/index.mjs +1 -1
  18. package/dist/hooks/index.d.mts +1 -1
  19. package/dist/{index-BL8CaQih.d.mts → index-TG7-pXDC.d.mts} +2 -2
  20. package/dist/{index-yhxyjqNb.d.mts → index-bX8T5bmn.d.mts} +4 -8
  21. package/dist/index.d.mts +5 -5
  22. package/dist/index.mjs +7 -6
  23. package/dist/integrations/event-gateway.mjs +1 -1
  24. package/dist/integrations/index.d.mts +1 -1
  25. package/dist/integrations/mcp/index.d.mts +4 -2
  26. package/dist/integrations/mcp/index.mjs +1 -1
  27. package/dist/integrations/mcp/testing.d.mts +1 -1
  28. package/dist/integrations/mcp/testing.mjs +1 -1
  29. package/dist/{interface-DGmPxakH.d.mts → interface-BnNjdl33.d.mts} +170 -8
  30. package/dist/org/index.d.mts +1 -1
  31. package/dist/org/index.mjs +1 -1
  32. package/dist/permissions/index.mjs +1 -1
  33. package/dist/{permissions-Jk5x3sxz.mjs → permissions-D9_AAtvz.mjs} +1 -1
  34. package/dist/plugins/index.d.mts +1 -1
  35. package/dist/plugins/index.mjs +3 -3
  36. package/dist/plugins/tracing-entry.mjs +1 -1
  37. package/dist/presets/index.d.mts +1 -1
  38. package/dist/presets/index.mjs +1 -1
  39. package/dist/presets/multiTenant.d.mts +1 -1
  40. package/dist/presets/multiTenant.mjs +1 -1
  41. package/dist/{presets-OMPaHMTY.mjs → presets-CD3e6M7c.mjs} +2 -2
  42. package/dist/registry/index.d.mts +1 -1
  43. package/dist/{resourceToTools-PMFE8HIv.mjs → resourceToTools-B1B1svLx.mjs} +81 -7
  44. package/dist/scope/index.d.mts +2 -2
  45. package/dist/scope/index.mjs +2 -2
  46. package/dist/{sse-BkViJPlT.mjs → sse-BF7GR7IB.mjs} +1 -1
  47. package/dist/testing/index.d.mts +2 -2
  48. package/dist/testing/index.mjs +1 -1
  49. package/dist/types/index.d.mts +3 -3
  50. package/dist/types/index.mjs +23 -2
  51. package/dist/{types-C6TQjtdi.mjs → types-BhtYdxZU.mjs} +26 -1
  52. package/dist/{types-BJmgxNbF.d.mts → types-ByCPlfZ1.d.mts} +1 -1
  53. package/dist/{types-Dt0-AI6E.d.mts → types-Guk83PDz.d.mts} +2 -2
  54. package/dist/utils/index.d.mts +2 -2
  55. package/dist/utils/index.mjs +1 -1
  56. package/package.json +4 -4
  57. package/skills/arc/SKILL.md +53 -2
  58. package/skills/arc/references/mcp.md +135 -0
@@ -1,4 +1,4 @@
1
- import { s as RequestScope } from "./elevation-Ca_yveIO.mjs";
1
+ import { s as RequestScope } from "./elevation-C_taLQrM.mjs";
2
2
  import { n as FieldPermissionMap } from "./fields-DFwdaWCq.mjs";
3
3
  import { i as UserBase, t as PermissionCheck } from "./types-BNUccdcf.mjs";
4
4
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, RouteHandlerMethod, RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
@@ -1073,6 +1073,46 @@ declare module 'fastify' {
1073
1073
  _ownershipCheck?: Record<string, unknown>;
1074
1074
  }
1075
1075
  }
1076
+ /**
1077
+ * Typed Fastify request with Arc decorations.
1078
+ *
1079
+ * Use this in `wrapHandler: false` handlers instead of `(req as any).user`.
1080
+ *
1081
+ * @example
1082
+ * ```typescript
1083
+ * import type { ArcRequest } from '@classytic/arc';
1084
+ *
1085
+ * handler: async (req: ArcRequest, reply: FastifyReply) => {
1086
+ * req.user?.id; // typed
1087
+ * req.scope.organizationId; // typed (when member)
1088
+ * req.signal; // AbortSignal (Fastify 5)
1089
+ * }
1090
+ * ```
1091
+ */
1092
+ type ArcRequest = FastifyRequest & {
1093
+ scope: RequestScope;
1094
+ user: Record<string, unknown> | undefined;
1095
+ signal: AbortSignal;
1096
+ };
1097
+ /**
1098
+ * Response envelope helper — wraps data in Arc's standard `{ success, data }` format.
1099
+ *
1100
+ * @example
1101
+ * ```typescript
1102
+ * import { envelope } from '@classytic/arc';
1103
+ *
1104
+ * handler: async (req, reply) => {
1105
+ * const data = await getResults();
1106
+ * return envelope(data);
1107
+ * // → { success: true, data }
1108
+ * }
1109
+ * ```
1110
+ */
1111
+ declare function envelope<T>(data: T, meta?: Record<string, unknown>): {
1112
+ success: true;
1113
+ data: T;
1114
+ [key: string]: unknown;
1115
+ };
1076
1116
  type AnyRecord = Record<string, unknown>;
1077
1117
  /** MongoDB ObjectId — accepts string or any object with a `toString()` (e.g. mongoose ObjectId). */
1078
1118
  type ObjectId = string | {
@@ -1475,6 +1515,22 @@ interface ResourceConfig<TDoc = AnyRecord> {
1475
1515
  skipValidation?: boolean;
1476
1516
  skipRegistry?: boolean;
1477
1517
  _appliedPresets?: string[];
1518
+ /**
1519
+ * Called during plugin registration with the scoped Fastify instance.
1520
+ * Use for wiring singletons, reading decorators, or setting up resource-specific
1521
+ * services that need access to the Fastify instance.
1522
+ *
1523
+ * @example
1524
+ * ```typescript
1525
+ * defineResource({
1526
+ * name: 'notification',
1527
+ * onRegister: (fastify) => {
1528
+ * setSseManager(fastify.sseManager);
1529
+ * },
1530
+ * })
1531
+ * ```
1532
+ */
1533
+ onRegister?: (fastify: FastifyInstance) => void | Promise<void>;
1478
1534
  /** HTTP method for update routes. Default: 'PATCH' */
1479
1535
  updateMethod?: 'PUT' | 'PATCH' | 'both';
1480
1536
  /**
@@ -1501,13 +1557,53 @@ interface ResourcePermissions {
1501
1557
  update?: PermissionCheck;
1502
1558
  delete?: PermissionCheck;
1503
1559
  }
1560
+ /**
1561
+ * Hook context passed to resource-level hook handlers.
1562
+ * Mirrors HookSystem's HookContext but with a simpler API for inline use.
1563
+ */
1564
+ interface ResourceHookContext {
1565
+ /** The document data (create/update body, or existing doc for delete) */
1566
+ data: AnyRecord;
1567
+ /** Authenticated user or null */
1568
+ user?: UserBase;
1569
+ /** Additional metadata (e.g. `{ id, existing }` for update/delete) */
1570
+ meta?: AnyRecord;
1571
+ }
1572
+ /**
1573
+ * Inline lifecycle hooks on a resource definition.
1574
+ * These are wired into the HookSystem automatically — same pipeline as presets and app-level hooks.
1575
+ *
1576
+ * @example
1577
+ * ```typescript
1578
+ * defineResource({
1579
+ * name: 'chat',
1580
+ * hooks: {
1581
+ * afterCreate: async (ctx) => {
1582
+ * analytics.track('chat.created', { chatId: ctx.data._id, userId: ctx.user?.id });
1583
+ * },
1584
+ * beforeDelete: async (ctx) => {
1585
+ * if (ctx.data.isProtected) throw new Error('Cannot delete protected chat');
1586
+ * },
1587
+ * afterDelete: async (ctx) => {
1588
+ * await notificationService.send('chat.deleted', { id: ctx.meta?.id });
1589
+ * },
1590
+ * },
1591
+ * });
1592
+ * ```
1593
+ */
1504
1594
  interface ResourceHooks {
1505
- beforeCreate?: (data: AnyRecord) => Promise<AnyRecord> | AnyRecord;
1506
- afterCreate?: (doc: AnyRecord) => Promise<void> | void;
1507
- beforeUpdate?: (id: string, data: AnyRecord) => Promise<AnyRecord> | AnyRecord;
1508
- afterUpdate?: (doc: AnyRecord) => Promise<void> | void;
1509
- beforeDelete?: (id: string) => Promise<void> | void;
1510
- afterDelete?: (id: string) => Promise<void> | void;
1595
+ /** Runs before create — can modify data by returning a new object */
1596
+ beforeCreate?: (ctx: ResourceHookContext) => Promise<AnyRecord | void> | AnyRecord | void;
1597
+ /** Runs after create receives the created document */
1598
+ afterCreate?: (ctx: ResourceHookContext) => Promise<void> | void;
1599
+ /** Runs before update — ctx.meta.id has the resource ID, ctx.meta.existing has the current doc */
1600
+ beforeUpdate?: (ctx: ResourceHookContext) => Promise<AnyRecord | void> | AnyRecord | void;
1601
+ /** Runs after update — receives the updated document */
1602
+ afterUpdate?: (ctx: ResourceHookContext) => Promise<void> | void;
1603
+ /** Runs before delete — ctx.data is the existing doc, ctx.meta.id has the resource ID */
1604
+ beforeDelete?: (ctx: ResourceHookContext) => Promise<void> | void;
1605
+ /** Runs after delete — ctx.data is the deleted doc, ctx.meta.id has the resource ID */
1606
+ afterDelete?: (ctx: ResourceHookContext) => Promise<void> | void;
1511
1607
  }
1512
1608
  /**
1513
1609
  * Additional route definition for custom endpoints
@@ -1556,6 +1652,41 @@ interface AdditionalRoute {
1556
1652
  * preHandler: (fastify) => [fastify.customerContext({ required: true })]
1557
1653
  */
1558
1654
  preHandler?: RouteHandlerMethod[] | ((fastify: FastifyInstance) => RouteHandlerMethod[]);
1655
+ /**
1656
+ * Pre-auth handlers — run BEFORE authentication middleware.
1657
+ * Use for promoting query params to headers (e.g., EventSource ?token= → Authorization).
1658
+ *
1659
+ * @example
1660
+ * ```typescript
1661
+ * preAuth: [(req) => {
1662
+ * const token = (req.query as Record<string, string>)?.token;
1663
+ * if (token) req.headers.authorization = `Bearer ${token}`;
1664
+ * }]
1665
+ * ```
1666
+ */
1667
+ preAuth?: RouteHandlerMethod[];
1668
+ /**
1669
+ * Streaming response mode — designed for SSE and AI streaming routes.
1670
+ * When `true`:
1671
+ * - Forces `wrapHandler: false` (no `{ success, data }` wrapper)
1672
+ * - Sets SSE headers: `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`
1673
+ * - `request.signal` (Fastify 5 built-in) is available for abort-on-disconnect
1674
+ *
1675
+ * @example
1676
+ * ```typescript
1677
+ * {
1678
+ * method: 'POST',
1679
+ * path: '/stream',
1680
+ * streamResponse: true,
1681
+ * permissions: requireAuth(),
1682
+ * handler: async (request, reply) => {
1683
+ * const { stream } = await generateStream({ abortSignal: request.signal });
1684
+ * return reply.send(stream);
1685
+ * },
1686
+ * }
1687
+ * ```
1688
+ */
1689
+ streamResponse?: boolean;
1559
1690
  /** Fastify route schema */
1560
1691
  schema?: Record<string, unknown>;
1561
1692
  /**
@@ -2126,12 +2257,43 @@ type TypedRepository<TDoc> = CrudRepository<TDoc>;
2126
2257
  *
2127
2258
  * CrudRepository<TDoc> and MongoKit Repository both satisfy this interface.
2128
2259
  */
2260
+ /**
2261
+ * Minimal repository interface for flexible adapter compatibility.
2262
+ * Any repository with these method signatures is accepted.
2263
+ *
2264
+ * **Required** — core CRUD (every resource needs these):
2265
+ * getAll, getById, create, update, delete
2266
+ *
2267
+ * **Recommended** — used by AccessControl for compound queries:
2268
+ * getOne
2269
+ *
2270
+ * **Optional** — enabled by presets, checked at runtime:
2271
+ * getBySlug — slugLookup preset
2272
+ * getDeleted — softDelete preset (list soft-deleted)
2273
+ * restore — softDelete preset (restore soft-deleted)
2274
+ * getTree — tree preset (hierarchical queries)
2275
+ * getChildren — tree preset (child nodes)
2276
+ * createMany — bulk preset (batch create)
2277
+ * updateMany — bulk preset (batch update by filter)
2278
+ * deleteMany — bulk preset (batch delete by filter)
2279
+ */
2129
2280
  interface RepositoryLike {
2130
2281
  getAll(params?: unknown): Promise<unknown>;
2131
2282
  getById(id: string, options?: unknown): Promise<unknown>;
2132
2283
  create(data: unknown, options?: unknown): Promise<unknown>;
2133
2284
  update(id: string, data: unknown, options?: unknown): Promise<unknown>;
2134
2285
  delete(id: string, options?: unknown): Promise<unknown>;
2286
+ /** Find single doc by compound filter — used by AccessControl for idField + org/policy scoping.
2287
+ * Without this, Arc falls back to getById + post-fetch security checks. */
2288
+ getOne?(filter: Record<string, unknown>, options?: unknown): Promise<unknown>;
2289
+ getBySlug?(slug: string, options?: unknown): Promise<unknown>;
2290
+ getDeleted?(options?: unknown): Promise<unknown>;
2291
+ restore?(id: string): Promise<unknown>;
2292
+ getTree?(options?: unknown): Promise<unknown>;
2293
+ getChildren?(parentId: string, options?: unknown): Promise<unknown>;
2294
+ createMany?(items: unknown[], options?: unknown): Promise<unknown>;
2295
+ updateMany?(filter: Record<string, unknown>, data: unknown): Promise<unknown>;
2296
+ deleteMany?(filter: Record<string, unknown>): Promise<unknown>;
2135
2297
  [key: string]: unknown;
2136
2298
  }
2137
2299
  interface DataAdapter<TDoc = unknown> {
@@ -2210,4 +2372,4 @@ interface ValidationResult {
2210
2372
  }
2211
2373
  type AdapterFactory<TDoc> = (config: unknown) => DataAdapter<TDoc>;
2212
2374
  //#endregion
2213
- export { RegistryStats as $, HookContext as $t, HealthCheck as A, FastifyHandler as At, MiddlewareConfig as B, InferDoc as Bt, EventDefinition as C, QueryResolverConfig as Ct, FastifyWithDecorators as D, AccessControlConfig as Dt, FastifyWithAuth as E, AccessControl as Et, IntrospectionData as F, RegisterOptions as Ft, ParsedQuery as G, Interceptor as Gt, ObjectId as H, PaginationParams as Ht, IntrospectionPluginOptions as I, ResourceRegistry as It, PresetHook as J, PipelineConfig as Jt, PopulateOption as K, NextFunction as Kt, JWTPayload as L, ResourceDefinition as Lt, InferAdapterDoc as M, IControllerResponse as Mt, InferDocType as N, IRequestContext as Nt, FieldRule as O, ControllerHandler as Ot, InferResourceDoc as P, RouteHandler as Pt, RegistryEntry as Q, DefineHookOptions as Qt, JwtContext as R, defineResource as Rt, CrudSchemas as S, QueryResolver as St, FastifyRequestExtras as T, BodySanitizerConfig as Tt, OpenApiSchemas as U, QueryOptions as Ut, MiddlewareHandler as V, PaginatedResult as Vt, OwnershipCheck as W, Guard as Wt, QueryParserInterface as X, PipelineStep as Xt, PresetResult as Y, PipelineContext as Yt, RateLimitConfig as Z, Transform as Zt, ConfigError as _, ValidateOptions as _t, RepositoryLike as a, HookSystemOptions as an, ResourceHooks as at, CrudRouteKey as b, BaseController as bt, AdditionalRoute as c, afterUpdate as cn, RouteHandlerMethod$1 as ct, ArcDecorator as d, beforeUpdate as dn, TokenPair as dt, HookHandler as en, RequestContext as et, ArcInternalMetadata as f, createHookSystem as fn, TypedController as ft, AuthenticatorContext as g, UserOrganization as gt, Authenticator as h, UserLike as ht, RelationMetadata as i, HookSystem as in, ResourceConfig as it, HealthOptions as j, IController as jt, GracefulShutdownOptions as k, ControllerLike as kt, AnyRecord as l, beforeCreate as ln, RouteSchemaOptions as lt, AuthPluginOptions as m, TypedResourceConfig as mt, DataAdapter as n, HookPhase as nn, RequestWithExtras as nt, SchemaMetadata as o, afterCreate as on, ResourceMetadata as ot, AuthHelpers as p, defineHook as pn, TypedRepository as pt, PresetFunction as q, OperationFilter as qt, FieldMetadata as r, HookRegistration as rn, ResourceCacheConfig as rt, ValidationResult as s, afterDelete as sn, ResourcePermissions as st, AdapterFactory as t, HookOperation as tn, RequestIdOptions as tt, ApiResponse as u, beforeDelete as un, ServiceContext as ut, ControllerQueryOptions as v, ValidationResult$1 as vt, EventsDecorator as w, BodySanitizer as wt, CrudRouterOptions as x, BaseControllerOptions as xt, CrudController as y, getUserId as yt, LookupOption as z, CrudRepository as zt };
2375
+ export { RegistryEntry as $, PipelineStep as $t, GracefulShutdownOptions as A, AccessControlConfig as At, LookupOption as B, ResourceDefinition as Bt, CrudSchemas as C, BaseController as Ct, FastifyWithAuth as D, BodySanitizer as Dt, FastifyRequestExtras as E, QueryResolverConfig as Et, InferResourceDoc as F, IControllerResponse as Ft, OwnershipCheck as G, PaginationParams as Gt, MiddlewareHandler as H, CrudRepository as Ht, IntrospectionData as I, IRequestContext as It, PresetFunction as J, Interceptor as Jt, ParsedQuery as K, QueryOptions as Kt, IntrospectionPluginOptions as L, RouteHandler as Lt, HealthOptions as M, ControllerLike as Mt, InferAdapterDoc as N, FastifyHandler as Nt, FastifyWithDecorators as O, BodySanitizerConfig as Ot, InferDocType as P, IController as Pt, RateLimitConfig as Q, PipelineContext as Qt, JWTPayload as R, RegisterOptions as Rt, CrudRouterOptions as S, getUserId as St, EventsDecorator as T, QueryResolver as Tt, ObjectId as U, InferDoc as Ut, MiddlewareConfig as V, defineResource as Vt, OpenApiSchemas as W, PaginatedResult as Wt, PresetResult as X, OperationFilter as Xt, PresetHook as Y, NextFunction as Yt, QueryParserInterface as Z, PipelineConfig as Zt, AuthenticatorContext as _, UserLike as _t, RepositoryLike as a, HookPhase as an, ResourceConfig as at, CrudController as b, ValidationResult$1 as bt, AdditionalRoute as c, HookSystemOptions as cn, ResourceMetadata as ct, ArcDecorator as d, afterUpdate as dn, RouteSchemaOptions as dt, Transform as en, RegistryStats as et, ArcInternalMetadata as f, beforeCreate as fn, ServiceContext as ft, Authenticator as g, defineHook as gn, TypedResourceConfig as gt, AuthPluginOptions as h, createHookSystem as hn, TypedRepository as ht, RelationMetadata as i, HookOperation as in, ResourceCacheConfig as it, HealthCheck as j, ControllerHandler as jt, FieldRule as k, AccessControl as kt, AnyRecord as l, afterCreate as ln, ResourcePermissions as lt, AuthHelpers as m, beforeUpdate as mn, TypedController as mt, DataAdapter as n, HookContext as nn, RequestIdOptions as nt, SchemaMetadata as o, HookRegistration as on, ResourceHookContext as ot, ArcRequest as p, beforeDelete as pn, TokenPair as pt, PopulateOption as q, Guard as qt, FieldMetadata as r, HookHandler as rn, RequestWithExtras as rt, ValidationResult as s, HookSystem as sn, ResourceHooks as st, AdapterFactory as t, DefineHookOptions as tn, RequestContext as tt, ApiResponse as u, afterDelete as un, RouteHandlerMethod$1 as ut, ConfigError as v, UserOrganization as vt, EventDefinition as w, BaseControllerOptions as wt, CrudRouteKey as x, envelope as xt, ControllerQueryOptions as y, ValidateOptions as yt, JwtContext as z, ResourceRegistry as zt };
@@ -1,4 +1,4 @@
1
- import { Pt as RouteHandler } from "../interface-DGmPxakH.mjs";
1
+ import { Lt as RouteHandler } from "../interface-BnNjdl33.mjs";
2
2
  import { i as UserBase } from "../types-BNUccdcf.mjs";
3
3
  import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
4
4
  import { FastifyPluginAsync, RouteHandlerMethod } from "fastify";
@@ -1,4 +1,4 @@
1
- import { c as hasOrgAccess, d as isMember, i as getOrgRoles, n as PUBLIC_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
1
+ import { a as getOrgRoles, d as isElevated, f as isMember, l as hasOrgAccess, n as PUBLIC_SCOPE } from "../types-BhtYdxZU.mjs";
2
2
  import fp from "fastify-plugin";
3
3
  //#region src/org/organizationPlugin.ts
4
4
  const DEFAULT_ROLES = [
@@ -1,4 +1,4 @@
1
1
  import { i as resolveEffectiveRoles, n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "../fields-ipsbIRPK.mjs";
2
2
  import { n as normalizeRoles, t as getUserRoles } from "../types-ZUu_h0jp.mjs";
3
- import { S as createRoleHierarchy, _ as ownerWithAdminBypass, a as createOrgPermissions, b as publicReadAdminWrite, c as requireOrgMembership, d as requireRoles, f as requireTeamMembership, g as fullPublic, h as authenticated, i as createDynamicPermissionMatrix, l as requireOrgRole, m as adminOnly, n as allowPublic, o as denyAll, p as when, r as anyOf, s as requireAuth, t as allOf, u as requireOwnership, v as presets_exports, x as readOnly, y as publicRead } from "../permissions-Jk5x3sxz.mjs";
3
+ import { S as createRoleHierarchy, _ as ownerWithAdminBypass, a as createOrgPermissions, b as publicReadAdminWrite, c as requireOrgMembership, d as requireRoles, f as requireTeamMembership, g as fullPublic, h as authenticated, i as createDynamicPermissionMatrix, l as requireOrgRole, m as adminOnly, n as allowPublic, o as denyAll, p as when, r as anyOf, s as requireAuth, t as allOf, u as requireOwnership, v as presets_exports, x as readOnly, y as publicRead } from "../permissions-D9_AAtvz.mjs";
4
4
  export { adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, authenticated, createDynamicPermissionMatrix, createOrgPermissions, createRoleHierarchy, denyAll, fields, fullPublic, getUserRoles, normalizeRoles, ownerWithAdminBypass, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, resolveEffectiveRoles, when };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { a as getTeamId, d as isMember, n as PUBLIC_SCOPE, o as getUserId, u as isElevated } from "./types-C6TQjtdi.mjs";
2
+ import { d as isElevated, f as isMember, n as PUBLIC_SCOPE, o as getTeamId, s as getUserId } from "./types-BhtYdxZU.mjs";
3
3
  import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
4
4
  import { t as MemoryCacheStore } from "./memory-BFAYkf8H.mjs";
5
5
  import { randomUUID } from "node:crypto";
@@ -1,4 +1,4 @@
1
- import { B as MiddlewareConfig, It as ResourceRegistry, J as PresetHook, c as AdditionalRoute, in as HookSystem, l as AnyRecord, lt as RouteSchemaOptions } from "../interface-DGmPxakH.mjs";
1
+ import { V as MiddlewareConfig, Y as PresetHook, c as AdditionalRoute, dt as RouteSchemaOptions, l as AnyRecord, sn as HookSystem, zt as ResourceRegistry } from "../interface-BnNjdl33.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "../externalPaths-DpO-s7r8.mjs";
3
3
  import { _ as cachingPlugin, a as versioningPlugin, c as MetricsOptions, d as SSEOptions, f as _default$6, g as _default$1, h as CachingRule, i as _default$7, l as _default$4, m as CachingOptions, n as errorHandlerPlugin, o as MetricEntry, p as ssePlugin, r as VersioningOptions, s as MetricsCollector, t as ErrorHandlerOptions, u as metricsPlugin } from "../errorHandler-Do4vVQ1f.mjs";
4
4
  import { t as TracingOptions } from "../tracing-bz_U4EM1.mjs";
@@ -1,13 +1,13 @@
1
1
  import { p as MUTATION_OPERATIONS } from "../constants-Cxde4rpC.mjs";
2
- import { r as getOrgId } from "../types-C6TQjtdi.mjs";
2
+ import { i as getOrgId } from "../types-BhtYdxZU.mjs";
3
3
  import { t as requestContext } from "../requestContext-DYtmNpm5.mjs";
4
4
  import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
5
5
  import { t as HookSystem } from "../HookSystem-COkyWztM.mjs";
6
6
  import { t as ResourceRegistry } from "../ResourceRegistry-DeCIFlix.mjs";
7
7
  import { n as caching_default, t as cachingPlugin } from "../caching-BSXB-Xr7.mjs";
8
- import { t as errorHandlerPlugin } from "../errorHandler-DMbGdzBG.mjs";
8
+ import { t as errorHandlerPlugin } from "../errorHandler-r2595m8T.mjs";
9
9
  import { n as metrics_default, t as metricsPlugin } from "../metrics-Csh4nsvv.mjs";
10
- import { n as sse_default, t as ssePlugin } from "../sse-BkViJPlT.mjs";
10
+ import { n as sse_default, t as ssePlugin } from "../sse-BF7GR7IB.mjs";
11
11
  import { n as versioning_default, t as versioningPlugin } from "../versioning-BzfeHmhj.mjs";
12
12
  import { randomUUID } from "node:crypto";
13
13
  import fp from "fastify-plugin";
@@ -44,7 +44,7 @@ try {
44
44
  function createTracerProvider(options) {
45
45
  if (!isAvailable) return null;
46
46
  const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
47
- const resolvedVersion = serviceVersion ?? "2.4.3";
47
+ const resolvedVersion = serviceVersion ?? "2.5.1";
48
48
  const exporter = new OTLPTraceExporter({ url: exporterUrl });
49
49
  const provider = new NodeTracerProvider({ resource: { attributes: {
50
50
  "service.name": serviceName,
@@ -1,4 +1,4 @@
1
- import { Mt as IControllerResponse, Nt as IRequestContext, Vt as PaginatedResult, Y as PresetResult, it as ResourceConfig, l as AnyRecord } from "../interface-DGmPxakH.mjs";
1
+ import { Ft as IControllerResponse, It as IRequestContext, Wt as PaginatedResult, X as PresetResult, at as ResourceConfig, l as AnyRecord } from "../interface-BnNjdl33.mjs";
2
2
  import { MultiTenantOptions, multiTenantPreset } from "./multiTenant.mjs";
3
3
 
4
4
  //#region src/presets/ownedByUser.d.ts
@@ -1,3 +1,3 @@
1
1
  import { multiTenantPreset } from "./multiTenant.mjs";
2
- import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-OMPaHMTY.mjs";
2
+ import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-CD3e6M7c.mjs";
3
3
  export { applyPresets, auditedPreset, bulkPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, slugLookupPreset, softDeletePreset, treePreset };
@@ -1,4 +1,4 @@
1
- import { Y as PresetResult, b as CrudRouteKey } from "../interface-DGmPxakH.mjs";
1
+ import { X as PresetResult, x as CrudRouteKey } from "../interface-BnNjdl33.mjs";
2
2
 
3
3
  //#region src/presets/multiTenant.d.ts
4
4
  interface MultiTenantOptions {
@@ -1,5 +1,5 @@
1
1
  import { o as DEFAULT_TENANT_FIELD } from "../constants-Cxde4rpC.mjs";
2
- import { d as isMember, n as PUBLIC_SCOPE, r as getOrgId, u as isElevated } from "../types-C6TQjtdi.mjs";
2
+ import { d as isElevated, f as isMember, i as getOrgId, n as PUBLIC_SCOPE } from "../types-BhtYdxZU.mjs";
3
3
  //#region src/presets/multiTenant.ts
4
4
  /** Read request.scope safely */
5
5
  function getScope(request) {
@@ -1,6 +1,6 @@
1
- import { n as PUBLIC_SCOPE, u as isElevated } from "./types-C6TQjtdi.mjs";
1
+ import { d as isElevated, n as PUBLIC_SCOPE } from "./types-BhtYdxZU.mjs";
2
2
  import { multiTenantPreset } from "./presets/multiTenant.mjs";
3
- import { d as requireRoles, n as allowPublic, s as requireAuth } from "./permissions-Jk5x3sxz.mjs";
3
+ import { d as requireRoles, n as allowPublic, s as requireAuth } from "./permissions-D9_AAtvz.mjs";
4
4
  //#region src/presets/ownedByUser.ts
5
5
  /**
6
6
  * Create ownership check middleware.
@@ -1,4 +1,4 @@
1
- import { Ft as RegisterOptions, I as IntrospectionPluginOptions, It as ResourceRegistry } from "../interface-DGmPxakH.mjs";
1
+ import { L as IntrospectionPluginOptions, Rt as RegisterOptions, zt as ResourceRegistry } from "../interface-BnNjdl33.mjs";
2
2
  import { FastifyPluginAsync } from "fastify";
3
3
 
4
4
  //#region src/registry/introspectionPlugin.d.ts
@@ -1,4 +1,4 @@
1
- import { t as BaseController } from "./BaseController-CkM5dUh_.mjs";
1
+ import { t as BaseController } from "./BaseController-CNwMYpDW.mjs";
2
2
  import { t as pluralize } from "./pluralize-CcT6qF0a.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/integrations/mcp/createMcpServer.ts
@@ -148,16 +148,55 @@ function typeToZod(type) {
148
148
  default: return z.string();
149
149
  }
150
150
  }
151
- /** Build list/query shape with filterable fields + pagination */
151
+ /** Operators that apply to numeric/date fields */
152
+ const COMPARISON_OPS = new Set([
153
+ "gt",
154
+ "gte",
155
+ "lt",
156
+ "lte"
157
+ ]);
158
+ /** Map operator to a human-readable description suffix */
159
+ function opDescription(op, fieldName) {
160
+ switch (op) {
161
+ case "gt": return `${fieldName} greater than`;
162
+ case "gte": return `${fieldName} greater than or equal`;
163
+ case "lt": return `${fieldName} less than`;
164
+ case "lte": return `${fieldName} less than or equal`;
165
+ case "ne": return `${fieldName} not equal to`;
166
+ case "in": return `${fieldName} in comma-separated list`;
167
+ case "nin": return `${fieldName} not in comma-separated list`;
168
+ case "exists": return `${fieldName} exists (true/false)`;
169
+ default: return `${fieldName} ${op}`;
170
+ }
171
+ }
172
+ /** Build list/query shape with filterable fields, operators, and pagination */
152
173
  function buildListShape(fieldRules, options) {
153
- const { filterableFields = [], hiddenFields = [], extraHideFields = [] } = options;
174
+ const { filterableFields = [], hiddenFields = [], extraHideFields = [], allowedOperators } = options;
154
175
  const allHidden = new Set([...hiddenFields, ...extraHideFields]);
155
176
  const shape = { ...PAGINATION_SHAPE };
156
- if (fieldRules) for (const name of filterableFields) {
177
+ if (!fieldRules) return shape;
178
+ for (const name of filterableFields) {
157
179
  if (allHidden.has(name)) continue;
158
180
  const rule = fieldRules[name];
159
181
  if (!rule) continue;
160
- shape[name] = buildFieldSchema(rule).optional();
182
+ const filterField = buildFieldSchema(rule);
183
+ shape[name] = filterField.optional();
184
+ if (allowedOperators?.length) {
185
+ const isNumericOrDate = rule.type === "number" || rule.type === "date";
186
+ for (const op of allowedOperators) {
187
+ if (COMPARISON_OPS.has(op) && !isNumericOrDate) continue;
188
+ if (op === "eq") continue;
189
+ if (op === "exists") {
190
+ shape[`${name}_${op}`] = z.boolean().optional().describe(opDescription(op, name));
191
+ continue;
192
+ }
193
+ if (op === "in" || op === "nin") {
194
+ shape[`${name}_${op}`] = z.string().optional().describe(opDescription(op, name));
195
+ continue;
196
+ }
197
+ shape[`${name}_${op}`] = filterField.optional().describe(opDescription(op, name));
198
+ }
199
+ }
161
200
  }
162
201
  return shape;
163
202
  }
@@ -193,7 +232,7 @@ function buildRequestContext(input, auth, operation, policyFilters) {
193
232
  case "list": return {
194
233
  ...base,
195
234
  params: {},
196
- query: { ...input },
235
+ query: expandOperatorKeys(input),
197
236
  body: void 0
198
237
  };
199
238
  case "get": return {
@@ -225,6 +264,40 @@ function buildRequestContext(input, auth, operation, policyFilters) {
225
264
  };
226
265
  }
227
266
  }
267
+ /** Convert MCP operator keys (`price_gt`) to MongoKit bracket notation (`price[gt]`). */
268
+ const OPERATOR_SUFFIXES = new Set([
269
+ "eq",
270
+ "ne",
271
+ "gt",
272
+ "gte",
273
+ "lt",
274
+ "lte",
275
+ "in",
276
+ "nin",
277
+ "exists"
278
+ ]);
279
+ function expandOperatorKeys(input) {
280
+ const out = {};
281
+ for (const [key, value] of Object.entries(input)) {
282
+ const lastUnderscore = key.lastIndexOf("_");
283
+ if (lastUnderscore > 0) {
284
+ const op = key.slice(lastUnderscore + 1);
285
+ if (OPERATOR_SUFFIXES.has(op)) {
286
+ const field = key.slice(0, lastUnderscore);
287
+ const existing = out[field];
288
+ if (existing && typeof existing === "object" && existing !== null) existing[op] = value;
289
+ else if (existing === void 0) out[field] = { [op]: value };
290
+ else out[field] = {
291
+ eq: existing,
292
+ [op]: value
293
+ };
294
+ continue;
295
+ }
296
+ }
297
+ out[key] = value;
298
+ }
299
+ return out;
300
+ }
228
301
  function buildScope(auth) {
229
302
  if (!auth) return { kind: "public" };
230
303
  if (auth.organizationId) return {
@@ -314,7 +387,8 @@ function resourceToTools(resource, config = {}) {
314
387
  hiddenFields,
315
388
  readonlyFields,
316
389
  extraHideFields: config.hideFields,
317
- filterableFields
390
+ filterableFields,
391
+ allowedOperators
318
392
  }),
319
393
  handler: createHandler(op, controller, resource.name, resource.permissions)
320
394
  });
@@ -1,4 +1,4 @@
1
- import { a as AUTHENTICATED_SCOPE, c as getOrgId, d as getUserId, f as getUserRoles, g as isMember, h as isElevated, i as elevationPlugin, l as getOrgRoles, m as isAuthenticated, n as ElevationOptions, o as PUBLIC_SCOPE, p as hasOrgAccess, r as _default, s as RequestScope, t as ElevationEvent, u as getTeamId } from "../elevation-Ca_yveIO.mjs";
1
+ import { _ as isMember, a as AUTHENTICATED_SCOPE, c as getOrgContext, d as getTeamId, f as getUserId, g as isElevated, h as isAuthenticated, i as elevationPlugin, l as getOrgId, m as hasOrgAccess, n as ElevationOptions, o as PUBLIC_SCOPE, p as getUserRoles, r as _default, s as RequestScope, t as ElevationEvent, u as getOrgRoles } from "../elevation-C_taLQrM.mjs";
2
2
  import { FastifyReply, FastifyRequest } from "fastify";
3
3
 
4
4
  //#region src/scope/rateLimitKey.d.ts
@@ -29,4 +29,4 @@ interface ResolveOrgFromHeaderOptions {
29
29
  */
30
30
  declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
31
31
  //#endregion
32
- export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
32
+ export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgContext, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
@@ -1,4 +1,4 @@
1
- import { a as getTeamId, c as hasOrgAccess, d as isMember, i as getOrgRoles, l as isAuthenticated, n as PUBLIC_SCOPE, o as getUserId, r as getOrgId, s as getUserRoles, t as AUTHENTICATED_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
1
+ import { a as getOrgRoles, c as getUserRoles, d as isElevated, f as isMember, i as getOrgId, l as hasOrgAccess, n as PUBLIC_SCOPE, o as getTeamId, r as getOrgContext, s as getUserId, t as AUTHENTICATED_SCOPE, u as isAuthenticated } from "../types-BhtYdxZU.mjs";
2
2
  import { n as normalizeRoles } from "../types-ZUu_h0jp.mjs";
3
3
  import { n as elevation_default, t as elevationPlugin } from "../elevation-BEdACOLB.mjs";
4
4
  //#region src/scope/rateLimitKey.ts
@@ -75,4 +75,4 @@ function resolveOrgFromHeader(options) {
75
75
  };
76
76
  }
77
77
  //#endregion
78
- export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
78
+ export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgContext, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { n as PUBLIC_SCOPE, r as getOrgId } from "./types-C6TQjtdi.mjs";
2
+ import { i as getOrgId, n as PUBLIC_SCOPE } from "./types-BhtYdxZU.mjs";
3
3
  import { t as arcLog } from "./logger-Dz3j1ItV.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/plugins/sse.ts
@@ -1,5 +1,5 @@
1
- import { Lt as ResourceDefinition, l as AnyRecord, zt as CrudRepository } from "../interface-DGmPxakH.mjs";
2
- import { r as CreateAppOptions } from "../types-Dt0-AI6E.mjs";
1
+ import { Bt as ResourceDefinition, Ht as CrudRepository, l as AnyRecord } from "../interface-BnNjdl33.mjs";
2
+ import { r as CreateAppOptions } from "../types-Guk83PDz.mjs";
3
3
  import Fastify, { FastifyInstance, FastifyServerOptions } from "fastify";
4
4
  import { Connection } from "mongoose";
5
5
  import { Mock } from "vitest";
@@ -1752,7 +1752,7 @@ function runEventTests(resourceName, displayName, events) {
1752
1752
  * ```
1753
1753
  */
1754
1754
  async function createTestApp(options = {}) {
1755
- const { createApp } = await import("../createApp-CBgVaFyh.mjs").then((n) => n.r);
1755
+ const { createApp } = await import("../createApp-oic3-ieX.mjs").then((n) => n.r);
1756
1756
  const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
1757
1757
  const defaultAuth = {
1758
1758
  type: "jwt",
@@ -1,4 +1,4 @@
1
- import { a as AUTHENTICATED_SCOPE, c as getOrgId, g as isMember, h as isElevated, l as getOrgRoles, m as isAuthenticated, n as ElevationOptions, o as PUBLIC_SCOPE, p as hasOrgAccess, s as RequestScope, t as ElevationEvent, u as getTeamId } from "../elevation-Ca_yveIO.mjs";
2
- import { $ as RegistryStats, A as HealthCheck, At as FastifyHandler, B as MiddlewareConfig, Bt as InferDoc, C as EventDefinition, D as FastifyWithDecorators, E as FastifyWithAuth, F as IntrospectionData, G as ParsedQuery, H as ObjectId, Ht as PaginationParams, I as IntrospectionPluginOptions, J as PresetHook, K as PopulateOption, L as JWTPayload, M as InferAdapterDoc, Mt as IControllerResponse, N as InferDocType, Nt as IRequestContext, O as FieldRule, Ot as ControllerHandler, P as InferResourceDoc, Pt as RouteHandler, Q as RegistryEntry, R as JwtContext, S as CrudSchemas, T as FastifyRequestExtras, U as OpenApiSchemas, Ut as QueryOptions, V as MiddlewareHandler, Vt as PaginatedResult, W as OwnershipCheck, X as QueryParserInterface, Y as PresetResult, Z as RateLimitConfig, _ as ConfigError, _t as ValidateOptions, at as ResourceHooks, b as CrudRouteKey, c as AdditionalRoute, ct as RouteHandlerMethod, d as ArcDecorator, dt as TokenPair, et as RequestContext, f as ArcInternalMetadata, ft as TypedController, g as AuthenticatorContext, gt as UserOrganization, h as Authenticator, ht as UserLike, it as ResourceConfig, j as HealthOptions, jt as IController, k as GracefulShutdownOptions, kt as ControllerLike, l as AnyRecord, lt as RouteSchemaOptions, m as AuthPluginOptions, mt as TypedResourceConfig, nt as RequestWithExtras, ot as ResourceMetadata, p as AuthHelpers, pt as TypedRepository, q as PresetFunction, rt as ResourceCacheConfig, st as ResourcePermissions, tt as RequestIdOptions, u as ApiResponse, ut as ServiceContext, v as ControllerQueryOptions, vt as ValidationResult, w as EventsDecorator, x as CrudRouterOptions, xt as BaseControllerOptions, y as CrudController, yt as getUserId, z as LookupOption, zt as CrudRepository } from "../interface-DGmPxakH.mjs";
1
+ import { _ as isMember, a as AUTHENTICATED_SCOPE, d as getTeamId, g as isElevated, h as isAuthenticated, l as getOrgId, m as hasOrgAccess, n as ElevationOptions, o as PUBLIC_SCOPE, s as RequestScope, t as ElevationEvent, u as getOrgRoles } from "../elevation-C_taLQrM.mjs";
2
+ import { $ as RegistryEntry, A as GracefulShutdownOptions, B as LookupOption, C as CrudSchemas, D as FastifyWithAuth, E as FastifyRequestExtras, F as InferResourceDoc, Ft as IControllerResponse, G as OwnershipCheck, Gt as PaginationParams, H as MiddlewareHandler, Ht as CrudRepository, I as IntrospectionData, It as IRequestContext, J as PresetFunction, K as ParsedQuery, Kt as QueryOptions, L as IntrospectionPluginOptions, Lt as RouteHandler, M as HealthOptions, Mt as ControllerLike, N as InferAdapterDoc, Nt as FastifyHandler, O as FastifyWithDecorators, P as InferDocType, Pt as IController, Q as RateLimitConfig, R as JWTPayload, S as CrudRouterOptions, St as getUserId, T as EventsDecorator, U as ObjectId, Ut as InferDoc, V as MiddlewareConfig, W as OpenApiSchemas, Wt as PaginatedResult, X as PresetResult, Y as PresetHook, Z as QueryParserInterface, _ as AuthenticatorContext, _t as UserLike, at as ResourceConfig, b as CrudController, bt as ValidationResult, c as AdditionalRoute, ct as ResourceMetadata, d as ArcDecorator, dt as RouteSchemaOptions, et as RegistryStats, f as ArcInternalMetadata, ft as ServiceContext, g as Authenticator, gt as TypedResourceConfig, h as AuthPluginOptions, ht as TypedRepository, it as ResourceCacheConfig, j as HealthCheck, jt as ControllerHandler, k as FieldRule, l as AnyRecord, lt as ResourcePermissions, m as AuthHelpers, mt as TypedController, nt as RequestIdOptions, ot as ResourceHookContext, p as ArcRequest, pt as TokenPair, q as PopulateOption, rt as RequestWithExtras, st as ResourceHooks, tt as RequestContext, u as ApiResponse, ut as RouteHandlerMethod, v as ConfigError, vt as UserOrganization, w as EventDefinition, wt as BaseControllerOptions, x as CrudRouteKey, xt as envelope, y as ControllerQueryOptions, yt as ValidateOptions, z as JwtContext } from "../interface-BnNjdl33.mjs";
3
3
  import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-BNUccdcf.mjs";
4
- export { AUTHENTICATED_SCOPE, AdditionalRoute, AnyRecord, ApiResponse, ArcDecorator, ArcInternalMetadata, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRepository, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OpenApiSchemas, OwnershipCheck, PUBLIC_SCOPE, PaginatedResult, PaginationParams, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryOptions, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResourceCacheConfig, ResourceConfig, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteHandler, RouteHandlerMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
4
+ export { AUTHENTICATED_SCOPE, AdditionalRoute, AnyRecord, ApiResponse, ArcDecorator, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRepository, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OpenApiSchemas, OwnershipCheck, PUBLIC_SCOPE, PaginatedResult, PaginationParams, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryOptions, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResourceCacheConfig, ResourceConfig, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteHandler, RouteHandlerMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult, envelope, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
@@ -1,6 +1,27 @@
1
- import { a as getTeamId, c as hasOrgAccess, d as isMember, i as getOrgRoles, l as isAuthenticated, n as PUBLIC_SCOPE, r as getOrgId, t as AUTHENTICATED_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
1
+ import { a as getOrgRoles, d as isElevated, f as isMember, i as getOrgId, l as hasOrgAccess, n as PUBLIC_SCOPE, o as getTeamId, t as AUTHENTICATED_SCOPE, u as isAuthenticated } from "../types-BhtYdxZU.mjs";
2
2
  //#region src/types/index.ts
3
3
  /**
4
+ * Response envelope helper — wraps data in Arc's standard `{ success, data }` format.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { envelope } from '@classytic/arc';
9
+ *
10
+ * handler: async (req, reply) => {
11
+ * const data = await getResults();
12
+ * return envelope(data);
13
+ * // → { success: true, data }
14
+ * }
15
+ * ```
16
+ */
17
+ function envelope(data, meta) {
18
+ return {
19
+ success: true,
20
+ data,
21
+ ...meta
22
+ };
23
+ }
24
+ /**
4
25
  * Extract user ID from a user object (supports both id and _id)
5
26
  */
6
27
  function getUserId(user) {
@@ -9,4 +30,4 @@ function getUserId(user) {
9
30
  return id ? String(id) : void 0;
10
31
  }
11
32
  //#endregion
12
- export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
33
+ export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, envelope, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
@@ -58,9 +58,34 @@ function getUserRoles(scope) {
58
58
  if (scope.kind === "member") return scope.userRoles;
59
59
  return [];
60
60
  }
61
+ /**
62
+ * Org context — canonical extraction from a Fastify request.
63
+ *
64
+ * Works regardless of auth type (JWT, Better Auth, custom) by reading
65
+ * `request.scope` and `request.user`. Eliminates the need for each resource
66
+ * to re-invent org extraction from headers/user/scope.
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * import { getOrgContext } from '@classytic/arc/scope';
71
+ *
72
+ * handler: async (request, reply) => {
73
+ * const { userId, organizationId, roles, orgRoles } = getOrgContext(request);
74
+ * }
75
+ * ```
76
+ */
77
+ function getOrgContext(request) {
78
+ const scope = request.scope ?? { kind: "public" };
79
+ return {
80
+ userId: getUserId(scope) ?? request.user?.id ?? request.user?._id,
81
+ organizationId: getOrgId(scope) ?? request.user?.organizationId ?? request.headers?.["x-organization-id"],
82
+ roles: getUserRoles(scope),
83
+ orgRoles: getOrgRoles(scope)
84
+ };
85
+ }
61
86
  /** Default public scope — used as initial decoration value */
62
87
  const PUBLIC_SCOPE = Object.freeze({ kind: "public" });
63
88
  /** Default authenticated scope — used when user is logged in but no org */
64
89
  const AUTHENTICATED_SCOPE = Object.freeze({ kind: "authenticated" });
65
90
  //#endregion
66
- export { getTeamId as a, hasOrgAccess as c, isMember as d, getOrgRoles as i, isAuthenticated as l, PUBLIC_SCOPE as n, getUserId as o, getOrgId as r, getUserRoles as s, AUTHENTICATED_SCOPE as t, isElevated as u };
91
+ export { getOrgRoles as a, getUserRoles as c, isElevated as d, isMember as f, getOrgId as i, hasOrgAccess as l, PUBLIC_SCOPE as n, getTeamId as o, getOrgContext as r, getUserId as s, AUTHENTICATED_SCOPE as t, isAuthenticated as u };