@classytic/arc 2.2.0 → 2.2.5

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.
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { a as RepositoryLike, i as RelationMetadata, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, t as AdapterFactory } from "../interface-Dm4-jnia.mjs";
2
+ import { a as RepositoryLike, i as RelationMetadata, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, t as AdapterFactory } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
- import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../prisma-Bi9nxirN.mjs";
4
+ import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../prisma-xjhMEq_S.mjs";
5
5
  export { type AdapterFactory, type DataAdapter, type FieldMetadata, MongooseAdapter, type MongooseAdapterOptions, PrismaAdapter, type PrismaAdapterOptions, type PrismaQueryOptions, PrismaQueryParser, type PrismaQueryParserOptions, type RelationMetadata, type RepositoryLike, type SchemaMetadata, type ValidationResult, createMongooseAdapter, createPrismaAdapter };
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { a as AuditContext, c as AuditStore, i as AuditAction, l as AuditStoreOptions, n as MongoAuditStoreOptions, o as AuditEntry, r as MongoConnection, s as AuditQueryOptions, u as createAuditEntry } from "../mongodb-ClykrfGo.mjs";
5
5
  import { FastifyPluginAsync } from "fastify";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { n as MongoAuditStoreOptions, t as MongoAuditStore } from "../mongodb-ClykrfGo.mjs";
5
5
  export { MongoAuditStore, type MongoAuditStoreOptions };
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import { t as PermissionCheck } from "../types-RLkFVgaw.mjs";
4
4
  import { AuthHelpers, AuthPluginOptions } from "../types/index.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "../externalPaths-SyPF2tgK.mjs";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { E as defineResource, T as ResourceDefinition, c as BaseController, d as QueryResolverConfig, f as BodySanitizer, h as AccessControlConfig, l as BaseControllerOptions, m as AccessControl, p as BodySanitizerConfig, u as QueryResolver } from "../interface-Dm4-jnia.mjs";
2
+ import { E as defineResource, T as ResourceDefinition, c as BaseController, d as QueryResolverConfig, f as BodySanitizer, h as AccessControlConfig, l as BaseControllerOptions, m as AccessControl, p as BodySanitizerConfig, u as QueryResolver } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
- import { A as createCrudRouter, C as MutationOperation, D as ActionRouterConfig, E as ActionHandler, O as IdempotencyService, S as MUTATION_OPERATIONS, T as SYSTEM_FIELDS, _ as HookOperation, a as getControllerScope, b as MAX_REGEX_LENGTH, c as CrudOperation, d as DEFAULT_MAX_LIMIT, f as DEFAULT_SORT, g as HOOK_PHASES, h as HOOK_OPERATIONS, i as getControllerContext, j as createPermissionMiddleware, k as createActionRouter, l as DEFAULT_ID_FIELD, m as DEFAULT_UPDATE_METHOD, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_TENANT_FIELD, r as createRequestContext, s as CRUD_OPERATIONS, t as createCrudHandlers, u as DEFAULT_LIMIT, v as HookPhase, w as RESERVED_QUERY_PARAMS, x as MAX_SEARCH_LENGTH, y as MAX_FILTER_DEPTH } from "../fastifyAdapter-DTOLNtjw.mjs";
4
+ import { A as createCrudRouter, C as MutationOperation, D as ActionRouterConfig, E as ActionHandler, O as IdempotencyService, S as MUTATION_OPERATIONS, T as SYSTEM_FIELDS, _ as HookOperation, a as getControllerScope, b as MAX_REGEX_LENGTH, c as CrudOperation, d as DEFAULT_MAX_LIMIT, f as DEFAULT_SORT, g as HOOK_PHASES, h as HOOK_OPERATIONS, i as getControllerContext, j as createPermissionMiddleware, k as createActionRouter, l as DEFAULT_ID_FIELD, m as DEFAULT_UPDATE_METHOD, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_TENANT_FIELD, r as createRequestContext, s as CRUD_OPERATIONS, t as createCrudHandlers, u as DEFAULT_LIMIT, v as HookPhase, w as RESERVED_QUERY_PARAMS, x as MAX_SEARCH_LENGTH, y as MAX_FILTER_DEPTH } from "../fastifyAdapter-CyAA2zlB.mjs";
5
5
  export { AccessControl, type AccessControlConfig, type ActionHandler, type ActionRouterConfig, BaseController, type BaseControllerOptions, BodySanitizer, type BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type IdempotencyService, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, getControllerContext, getControllerScope, sendControllerResponse };
@@ -1,4 +1,4 @@
1
1
  import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-DdXFXQtN.mjs";
2
- import { _ as QueryResolver, c as createPermissionMiddleware, d as createFastifyHandler, f as createRequestContext, g as BaseController, h as sendControllerResponse, m as getControllerScope, n as defineResource, o as createActionRouter, p as getControllerContext, s as createCrudRouter, t as ResourceDefinition, u as createCrudHandlers, v as BodySanitizer, y as AccessControl } from "../defineResource-Bq_fXZtm.mjs";
2
+ import { _ as QueryResolver, c as createPermissionMiddleware, d as createFastifyHandler, f as createRequestContext, g as BaseController, h as sendControllerResponse, m as getControllerScope, n as defineResource, o as createActionRouter, p as getControllerContext, s as createCrudRouter, t as ResourceDefinition, u as createCrudHandlers, v as BodySanitizer, y as AccessControl } from "../defineResource-DO9ONe_D.mjs";
3
3
 
4
4
  export { AccessControl, BaseController, BodySanitizer, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, getControllerContext, getControllerScope, sendControllerResponse };
@@ -479,10 +479,34 @@ async function createApp(options) {
479
479
  fastify.log.debug("Custom authentication plugin enabled");
480
480
  break;
481
481
  case "authenticator": {
482
- const { authenticate } = authConfig;
482
+ const { authenticate, optionalAuthenticate } = authConfig;
483
483
  fastify.decorate("authenticate", async function(request, reply) {
484
484
  await authenticate(request, reply);
485
485
  });
486
+ if (!fastify.hasDecorator("optionalAuthenticate")) if (optionalAuthenticate) fastify.decorate("optionalAuthenticate", async function(request, reply) {
487
+ await optionalAuthenticate(request, reply);
488
+ });
489
+ else fastify.decorate("optionalAuthenticate", async function(request, reply) {
490
+ let intercepted = false;
491
+ const proxyReply = new Proxy(reply, { get(target, prop) {
492
+ if (prop === "code") return (statusCode) => {
493
+ if (statusCode === 401 || statusCode === 403) {
494
+ intercepted = true;
495
+ return new Proxy(target, { get(_t, p) {
496
+ if (p === "send" || p === "type" || p === "header" || p === "headers") return () => proxyReply;
497
+ return Reflect.get(target, p, target);
498
+ } });
499
+ }
500
+ return target.code(statusCode);
501
+ };
502
+ if (prop === "send" && intercepted) return () => proxyReply;
503
+ if (prop === "sent") return intercepted ? false : target.sent;
504
+ return Reflect.get(target, prop, target);
505
+ } });
506
+ try {
507
+ await authenticate(request, proxyReply);
508
+ } catch {}
509
+ });
486
510
  trackPlugin("auth-authenticator");
487
511
  fastify.log.debug("Custom authenticator enabled");
488
512
  break;
@@ -107,6 +107,18 @@ var AccessControl = class AccessControl {
107
107
  throw error;
108
108
  }
109
109
  }
110
+ /**
111
+ * Post-fetch access control validation for items fetched by non-ID queries
112
+ * (e.g., getBySlug, restore). Applies org scope, policy filters, and
113
+ * ownership checks — the same guarantees as fetchWithAccessControl.
114
+ */
115
+ validateItemAccess(item, req) {
116
+ if (!item) return false;
117
+ const arcContext = this._meta(req);
118
+ if (!this.checkOrgScope(item, arcContext)) return false;
119
+ if (!this.checkPolicyFilters(item, req)) return false;
120
+ return true;
121
+ }
110
122
  /** Extract typed Arc internal metadata from request */
111
123
  _meta(req) {
112
124
  return req.metadata;
@@ -161,16 +173,20 @@ var AccessControl = class AccessControl {
161
173
  * Supports: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex, $and, $or
162
174
  */
163
175
  defaultMatchesPolicyFilters(item, policyFilters) {
164
- if (policyFilters.$and && Array.isArray(policyFilters.$and)) return policyFilters.$and.every((condition) => {
165
- return Object.entries(condition).every(([key, value]) => {
166
- return this.matchesFilter(item, key, value);
167
- });
168
- });
169
- if (policyFilters.$or && Array.isArray(policyFilters.$or)) return policyFilters.$or.some((condition) => {
170
- return Object.entries(condition).every(([key, value]) => {
171
- return this.matchesFilter(item, key, value);
172
- });
173
- });
176
+ if (policyFilters.$and && Array.isArray(policyFilters.$and)) {
177
+ if (!policyFilters.$and.every((condition) => {
178
+ return Object.entries(condition).every(([key, value]) => {
179
+ return this.matchesFilter(item, key, value);
180
+ });
181
+ })) return false;
182
+ }
183
+ if (policyFilters.$or && Array.isArray(policyFilters.$or)) {
184
+ if (!policyFilters.$or.some((condition) => {
185
+ return Object.entries(condition).every(([key, value]) => {
186
+ return this.matchesFilter(item, key, value);
187
+ });
188
+ })) return false;
189
+ }
174
190
  for (const [key, value] of Object.entries(policyFilters)) {
175
191
  if (key.startsWith("$")) continue;
176
192
  if (!this.matchesFilter(item, key, value)) return false;
@@ -782,9 +798,8 @@ var BaseController = class {
782
798
  const slugField = this._presetFields.slugField ?? "slug";
783
799
  const slug = req.params[slugField] ?? req.params.slug;
784
800
  const options = this.queryResolver.resolve(req, this.meta(req));
785
- const arcContext = this.meta(req);
786
801
  const item = await repo.getBySlug(slug, options);
787
- if (!item || !this.accessControl.checkOrgScope(item, arcContext)) return {
802
+ if (!this.accessControl.validateItemAccess(item, req)) return {
788
803
  success: false,
789
804
  error: "Resource not found",
790
805
  status: 404
@@ -836,6 +851,18 @@ var BaseController = class {
836
851
  error: "ID parameter is required",
837
852
  status: 400
838
853
  };
854
+ const existing = await this.accessControl.fetchWithAccessControl(id, req, repo);
855
+ if (!existing) return {
856
+ success: false,
857
+ error: "Resource not found",
858
+ status: 404
859
+ };
860
+ if (!this.accessControl.checkOwnership(existing, req)) return {
861
+ success: false,
862
+ error: "You do not have permission to restore this resource",
863
+ details: { code: "OWNERSHIP_DENIED" },
864
+ status: 403
865
+ };
839
866
  const item = await repo.restore(id);
840
867
  if (!item) return {
841
868
  success: false,
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { RegistryEntry } from "../types/index.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "../externalPaths-SyPF2tgK.mjs";
@@ -1,10 +1,10 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import "../queryCachePlugin-Q6SYuHZ6.mjs";
5
5
  import "../eventPlugin-H6wDDjGO.mjs";
6
6
  import "../errorHandler-CW3OOeYq.mjs";
7
- import { a as CustomPluginAuthOption, c as RawBodyOptions, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption } from "../types-B0dhNrnd.mjs";
7
+ import { a as CustomPluginAuthOption, c as RawBodyOptions, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption } from "../types-DMSBMkaZ.mjs";
8
8
  import { FastifyInstance } from "fastify";
9
9
 
10
10
  //#region src/factory/createApp.d.ts
@@ -1,3 +1,3 @@
1
- import { a as getPreset, i as developmentPreset, n as createApp, o as productionPreset, s as testingPreset, t as ArcFactory } from "../createApp-BTHYuHNU.mjs";
1
+ import { a as getPreset, i as developmentPreset, n as createApp, o as productionPreset, s as testingPreset, t as ArcFactory } from "../createApp-BKHSl2nT.mjs";
2
2
 
3
3
  export { ArcFactory, createApp, developmentPreset, getPreset, productionPreset, testingPreset };
@@ -1,5 +1,5 @@
1
1
  import { s as RequestScope } from "./elevation-DGo5shaX.mjs";
2
- import { b as IControllerResponse, x as IRequestContext, y as IController } from "./interface-Dm4-jnia.mjs";
2
+ import { b as IControllerResponse, x as IRequestContext, y as IController } from "./interface-DZYNK9bb.mjs";
3
3
  import { t as PermissionCheck } from "./types-RLkFVgaw.mjs";
4
4
  import { CrudController, CrudRouterOptions, FastifyWithDecorators, RequestContext, RequestWithExtras } from "./types/index.mjs";
5
5
  import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
@@ -1,4 +1,4 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { $ as beforeUpdate, B as DefineHookOptions, G as HookRegistration, H as HookHandler, J as afterCreate, K as HookSystem, Q as beforeDelete, U as HookOperation, V as HookContext, W as HookPhase, X as afterUpdate, Y as afterDelete, Z as beforeCreate, et as createHookSystem, q as HookSystemOptions, tt as defineHook } from "../interface-Dm4-jnia.mjs";
2
+ import { $ as beforeUpdate, B as DefineHookOptions, G as HookRegistration, H as HookHandler, J as afterCreate, K as HookSystem, Q as beforeDelete, U as HookOperation, V as HookContext, W as HookPhase, X as afterUpdate, Y as afterDelete, Z as beforeCreate, et as createHookSystem, q as HookSystemOptions, tt as defineHook } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import "./elevation-DGo5shaX.mjs";
2
- import { D as CrudRepository, E as defineResource, F as OperationFilter, I as PipelineConfig, L as PipelineContext, M as Guard, N as Interceptor, P as NextFunction, R as PipelineStep, S as RouteHandler, T as ResourceDefinition, _ as ControllerLike, a as RepositoryLike, b as IControllerResponse, c as BaseController, i as RelationMetadata, j as QueryOptions, k as PaginatedResult, l as BaseControllerOptions, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, x as IRequestContext, y as IController, z as Transform } from "./interface-Dm4-jnia.mjs";
2
+ import { D as CrudRepository, E as defineResource, F as OperationFilter, I as PipelineConfig, L as PipelineContext, M as Guard, N as Interceptor, P as NextFunction, R as PipelineStep, S as RouteHandler, T as ResourceDefinition, _ as ControllerLike, a as RepositoryLike, b as IControllerResponse, c as BaseController, i as RelationMetadata, j as QueryOptions, k as PaginatedResult, l as BaseControllerOptions, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, x as IRequestContext, y as IController, z as Transform } from "./interface-DZYNK9bb.mjs";
3
3
  import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-Bi_AVKSo.mjs";
4
4
  import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-RLkFVgaw.mjs";
5
5
  import { AdditionalRoute, AnyRecord, ApiResponse, ArcInternalMetadata, AuthPluginOptions, ConfigError, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, EventDefinition, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, MiddlewareConfig, MiddlewareHandler, OwnershipCheck, PresetFunction, PresetResult, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestWithExtras, ResourceConfig, ResourceMetadata, RouteHandlerMethod, RouteSchemaOptions, ServiceContext, TypedController, TypedRepository, TypedResourceConfig, UserOrganization, ValidateOptions, ValidationResult as ValidationResult$1 } from "./types/index.mjs";
6
- import { c as MongooseAdapterOptions, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./prisma-Bi9nxirN.mjs";
6
+ import { c as MongooseAdapterOptions, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./prisma-xjhMEq_S.mjs";
7
7
  import "./adapters/index.mjs";
8
- import { C as MutationOperation, S as MUTATION_OPERATIONS, T as SYSTEM_FIELDS, _ as HookOperation, a as getControllerScope, b as MAX_REGEX_LENGTH, c as CrudOperation, d as DEFAULT_MAX_LIMIT, f as DEFAULT_SORT, g as HOOK_PHASES, h as HOOK_OPERATIONS, l as DEFAULT_ID_FIELD, m as DEFAULT_UPDATE_METHOD, p as DEFAULT_TENANT_FIELD, s as CRUD_OPERATIONS, u as DEFAULT_LIMIT, v as HookPhase, w as RESERVED_QUERY_PARAMS, x as MAX_SEARCH_LENGTH, y as MAX_FILTER_DEPTH } from "./fastifyAdapter-DTOLNtjw.mjs";
8
+ import { C as MutationOperation, S as MUTATION_OPERATIONS, T as SYSTEM_FIELDS, _ as HookOperation, a as getControllerScope, b as MAX_REGEX_LENGTH, c as CrudOperation, d as DEFAULT_MAX_LIMIT, f as DEFAULT_SORT, g as HOOK_PHASES, h as HOOK_OPERATIONS, l as DEFAULT_ID_FIELD, m as DEFAULT_UPDATE_METHOD, p as DEFAULT_TENANT_FIELD, s as CRUD_OPERATIONS, u as DEFAULT_LIMIT, v as HookPhase, w as RESERVED_QUERY_PARAMS, x as MAX_SEARCH_LENGTH, y as MAX_FILTER_DEPTH } from "./fastifyAdapter-CyAA2zlB.mjs";
9
9
  import "./core/index.mjs";
10
10
  import { a as NotFoundError, d as ValidationError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-DAWRdiYP.mjs";
11
11
  import { a as presets_d_exports, c as readOnly, i as ownerWithAdminBypass, n as authenticated, o as publicRead, r as fullPublic, s as publicReadAdminWrite, t as adminOnly } from "./presets-BTeYbw7h.mjs";
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "./constants-DdXFXQtN.mjs";
2
2
  import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./prisma-DJbMt3yf.mjs";
3
- import { a as validateResourceConfig, g as BaseController, i as formatValidationErrors, l as pipe, m as getControllerScope, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-Bq_fXZtm.mjs";
3
+ import { a as validateResourceConfig, g as BaseController, i as formatValidationErrors, l as pipe, m as getControllerScope, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-DO9ONe_D.mjs";
4
4
  import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-CTd_CrKr.mjs";
5
5
  import { i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-DBANPbGr.mjs";
6
6
  import { t as requestContext } from "./requestContext-xi6OKBL-.mjs";
@@ -818,6 +818,12 @@ declare class AccessControl {
818
818
  * buildIdFilter -> getOne (or getById + checkOrgScope + checkPolicyFilters)
819
819
  */
820
820
  fetchWithAccessControl<TDoc>(id: string, req: IRequestContext, repository: AccessControlRepository, queryOptions?: unknown): Promise<TDoc | null>;
821
+ /**
822
+ * Post-fetch access control validation for items fetched by non-ID queries
823
+ * (e.g., getBySlug, restore). Applies org scope, policy filters, and
824
+ * ownership checks — the same guarantees as fetchWithAccessControl.
825
+ */
826
+ validateItemAccess(item: AnyRecord | null, req: IRequestContext): boolean;
821
827
  /** Extract typed Arc internal metadata from request */
822
828
  private _meta;
823
829
  /**
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { S as RouteHandler } from "../interface-Dm4-jnia.mjs";
2
+ import { S as RouteHandler } from "../interface-DZYNK9bb.mjs";
3
3
  import { i as UserBase } from "../types-RLkFVgaw.mjs";
4
4
  import "../types/index.mjs";
5
5
  import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { K as HookSystem, w as ResourceRegistry } from "../interface-Dm4-jnia.mjs";
2
+ import { K as HookSystem, w as ResourceRegistry } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { AdditionalRoute, AnyRecord, MiddlewareConfig, PresetHook, RouteSchemaOptions } from "../types/index.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "../externalPaths-SyPF2tgK.mjs";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { b as IControllerResponse, k as PaginatedResult, x as IRequestContext } from "../interface-Dm4-jnia.mjs";
2
+ import { b as IControllerResponse, k as PaginatedResult, x as IRequestContext } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { AnyRecord, PresetResult, ResourceConfig } from "../types/index.mjs";
5
5
  import multiTenantPreset, { MultiTenantOptions } from "./multiTenant.mjs";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { CrudRouteKey, PresetResult } from "../types/index.mjs";
5
5
 
@@ -1,4 +1,4 @@
1
- import { D as CrudRepository, a as RepositoryLike, n as DataAdapter, o as SchemaMetadata, s as ValidationResult } from "./interface-Dm4-jnia.mjs";
1
+ import { D as CrudRepository, a as RepositoryLike, n as DataAdapter, o as SchemaMetadata, s as ValidationResult } from "./interface-DZYNK9bb.mjs";
2
2
  import { OpenApiSchemas, ParsedQuery, QueryParserInterface, RouteSchemaOptions } from "./types/index.mjs";
3
3
  import { Model } from "mongoose";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { C as RegisterOptions, w as ResourceRegistry } from "../interface-Dm4-jnia.mjs";
2
+ import { C as RegisterOptions, w as ResourceRegistry } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { IntrospectionPluginOptions } from "../types/index.mjs";
5
5
  import { FastifyPluginAsync } from "fastify";
@@ -1,11 +1,11 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { D as CrudRepository, T as ResourceDefinition } from "../interface-Dm4-jnia.mjs";
2
+ import { D as CrudRepository, T as ResourceDefinition } from "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { AnyRecord } from "../types/index.mjs";
5
5
  import "../queryCachePlugin-Q6SYuHZ6.mjs";
6
6
  import "../eventPlugin-H6wDDjGO.mjs";
7
7
  import "../errorHandler-CW3OOeYq.mjs";
8
- import { r as CreateAppOptions } from "../types-B0dhNrnd.mjs";
8
+ import { r as CreateAppOptions } from "../types-DMSBMkaZ.mjs";
9
9
  import Fastify, { FastifyInstance } from "fastify";
10
10
  import { Mock } from "vitest";
11
11
  import { Connection } from "mongoose";
@@ -1000,7 +1000,7 @@ var DatabaseSnapshot = class {
1000
1000
  * ```
1001
1001
  */
1002
1002
  async function createTestApp(options = {}) {
1003
- const { createApp } = await import("../createApp-BTHYuHNU.mjs").then((n) => n.r);
1003
+ const { createApp } = await import("../createApp-BKHSl2nT.mjs").then((n) => n.r);
1004
1004
  const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
1005
1005
  const defaultAuth = {
1006
1006
  type: "jwt",
@@ -1,5 +1,5 @@
1
1
  import { a as AUTHENTICATED_SCOPE, c as getOrgId, d as hasOrgAccess, f as isAuthenticated, l as getOrgRoles, m as isMember, n as ElevationOptions, o as PUBLIC_SCOPE, p as isElevated, s as RequestScope, t as ElevationEvent, u as getTeamId } from "../elevation-DGo5shaX.mjs";
2
- import { A as PaginationParams, D as CrudRepository, I as PipelineConfig, K as HookSystem, O as InferDoc, S as RouteHandler, _ as ControllerLike, b as IControllerResponse, g as ControllerHandler, j as QueryOptions, k as PaginatedResult, l as BaseControllerOptions, n as DataAdapter, v as FastifyHandler, w as ResourceRegistry, x as IRequestContext, y as IController } from "../interface-Dm4-jnia.mjs";
2
+ import { A as PaginationParams, D as CrudRepository, I as PipelineConfig, K as HookSystem, O as InferDoc, S as RouteHandler, _ as ControllerLike, b as IControllerResponse, g as ControllerHandler, j as QueryOptions, k as PaginatedResult, l as BaseControllerOptions, n as DataAdapter, v as FastifyHandler, w as ResourceRegistry, x as IRequestContext, y as IController } from "../interface-DZYNK9bb.mjs";
3
3
  import { n as FieldPermissionMap } from "../fields-Bi_AVKSo.mjs";
4
4
  import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-RLkFVgaw.mjs";
5
5
  import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod, RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
@@ -154,6 +154,13 @@ interface CustomAuthenticatorOption {
154
154
  type: 'authenticator';
155
155
  /** Authenticate function — decorates fastify.authenticate directly */
156
156
  authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
157
+ /**
158
+ * Optional authenticate function for public routes.
159
+ * If not provided, Arc auto-generates one by wrapping `authenticate` and
160
+ * intercepting 401/403 responses so unauthenticated requests proceed as public.
161
+ * Provide this if your authenticator has side effects that shouldn't run on public routes.
162
+ */
163
+ optionalAuthenticate?: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
157
164
  }
158
165
  /**
159
166
  * All supported auth configuration shapes
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Dm4-jnia.mjs";
2
+ import "../interface-DZYNK9bb.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { AnyRecord, OpenApiSchemas, ParsedQuery, QueryParserInterface } from "../types/index.mjs";
5
5
  import { a as NotFoundError, c as RateLimitError, d as ValidationError, f as createError, i as ForbiddenError, l as ServiceUnavailableError, n as ConflictError, o as OrgAccessDeniedError, p as isArcError, r as ErrorDetails, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-DAWRdiYP.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/arc",
3
- "version": "2.2.0",
3
+ "version": "2.2.5",
4
4
  "description": "Resource-oriented backend framework for Fastify — clean, minimal, powerful, tree-shakable",
5
5
  "type": "module",
6
6
  "exports": {