@classytic/arc 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-Cb2klgid.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";
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-DQBSSHAB.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";
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-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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";
@@ -2174,7 +2174,17 @@ ${orgPluginUsage}
2174
2174
  enabled: process.env.NODE_ENV === 'production',
2175
2175
  },
2176
2176
  });
2177
- }
2177
+ ${config.adapter === "mongokit" ? `
2178
+ // Register stub Mongoose models for Better Auth collections.
2179
+ // BA uses the raw MongoDB driver, so no Mongoose models exist by default.
2180
+ // These stubs (strict: false) enable populate() on refs like 'user', 'organization', etc.
2181
+ const baCollections = ['user', 'organization', 'member', 'invitation', 'session', 'account'];
2182
+ for (const name of baCollections) {
2183
+ if (!mongoose.models[name]) {
2184
+ mongoose.model(name, new mongoose.Schema({}, { strict: false, collection: name }));
2185
+ }
2186
+ }
2187
+ ` : ""} }
2178
2188
 
2179
2189
  return _auth;
2180
2190
  }
@@ -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-Cb2klgid.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";
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-sGkvUvf5.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";
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-DZVbwsFb.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";
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 };
@@ -50,7 +50,9 @@ const productionPreset = {
50
50
  allowedHeaders: [
51
51
  "Content-Type",
52
52
  "Authorization",
53
- "Accept"
53
+ "Accept",
54
+ "x-organization-id",
55
+ "x-request-id"
54
56
  ]
55
57
  },
56
58
  rateLimit: {
@@ -95,7 +97,9 @@ const developmentPreset = {
95
97
  allowedHeaders: [
96
98
  "Content-Type",
97
99
  "Authorization",
98
- "Accept"
100
+ "Accept",
101
+ "x-organization-id",
102
+ "x-request-id"
99
103
  ]
100
104
  },
101
105
  rateLimit: {
@@ -337,8 +341,9 @@ async function createApp(options) {
337
341
  } else fastify.log.warn("Helmet disabled - security headers not applied");
338
342
  if (config.cors !== false) {
339
343
  const cors = await loadPlugin("cors");
340
- const corsOptions = config.cors ?? {};
344
+ const corsOptions = { ...config.cors ?? {} };
341
345
  if (config.preset === "production" && (!corsOptions || !("origin" in corsOptions))) throw new Error("CORS origin must be explicitly configured in production.\nSet cors.origin to allowed domains or set cors: false to disable.\nExample: cors: { origin: ['https://yourdomain.com'] }\nDocs: https://github.com/classytic/arc#security");
346
+ if (corsOptions.credentials && corsOptions.origin === "*") corsOptions.origin = true;
342
347
  await fastify.register(cors, corsOptions);
343
348
  fastify.log.debug("CORS enabled");
344
349
  } else fastify.log.warn("CORS disabled");
@@ -397,6 +397,16 @@ var BaseController = class {
397
397
  this.update = this.update.bind(this);
398
398
  this.delete = this.delete.bind(this);
399
399
  }
400
+ /**
401
+ * Get the tenant field name if multi-tenant scoping is enabled.
402
+ * Returns `undefined` when `tenantField` is `false` (platform-universal mode).
403
+ *
404
+ * Use this in subclass overrides instead of accessing `this.tenantField` directly
405
+ * to avoid TypeScript indexing errors with `string | false`.
406
+ */
407
+ getTenantField() {
408
+ return this.tenantField || void 0;
409
+ }
400
410
  /** Extract typed Arc internal metadata from request */
401
411
  meta(req) {
402
412
  return req.metadata;
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import "../queryCachePlugin-Q6SYuHZ6.mjs";
5
5
  import "../eventPlugin-H6wDDjGO.mjs";
@@ -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-D2D5XXaV.mjs";
1
+ import { a as getPreset, i as developmentPreset, n as createApp, o as productionPreset, s as testingPreset, t as ArcFactory } from "../createApp-BTHYuHNU.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-Cb2klgid.mjs";
2
+ import { b as IControllerResponse, x as IRequestContext, y as IController } from "./interface-Dm4-jnia.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-Cb2klgid.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";
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-Cb2klgid.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";
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-DQBSSHAB.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";
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-sGkvUvf5.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";
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-DZVbwsFb.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";
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";
@@ -978,6 +978,14 @@ declare class BaseController<TDoc = AnyRecord, TRepository extends RepositoryLik
978
978
  private _presetFields;
979
979
  private _cacheConfig?;
980
980
  constructor(repository: TRepository, options?: BaseControllerOptions);
981
+ /**
982
+ * Get the tenant field name if multi-tenant scoping is enabled.
983
+ * Returns `undefined` when `tenantField` is `false` (platform-universal mode).
984
+ *
985
+ * Use this in subclass overrides instead of accessing `this.tenantField` directly
986
+ * to avoid TypeScript indexing errors with `string | false`.
987
+ */
988
+ protected getTenantField(): string | undefined;
981
989
  /** Extract typed Arc internal metadata from request */
982
990
  private meta;
983
991
  /** Get hook system from request context (instance-scoped) */
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { S as RouteHandler } from "../interface-Cb2klgid.mjs";
2
+ import { S as RouteHandler } from "../interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import { K as HookSystem, w as ResourceRegistry } from "../interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import { b as IControllerResponse, k as PaginatedResult, x as IRequestContext } from "../interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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-Cb2klgid.mjs";
1
+ import { D as CrudRepository, a as RepositoryLike, n as DataAdapter, o as SchemaMetadata, s as ValidationResult } from "./interface-Dm4-jnia.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-Cb2klgid.mjs";
2
+ import { C as RegisterOptions, w as ResourceRegistry } from "../interface-Dm4-jnia.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { IntrospectionPluginOptions } from "../types/index.mjs";
5
5
  import { FastifyPluginAsync } from "fastify";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import { D as CrudRepository, T as ResourceDefinition } from "../interface-Cb2klgid.mjs";
2
+ import { D as CrudRepository, T as ResourceDefinition } from "../interface-Dm4-jnia.mjs";
3
3
  import "../types-RLkFVgaw.mjs";
4
4
  import { AnyRecord } from "../types/index.mjs";
5
5
  import "../queryCachePlugin-Q6SYuHZ6.mjs";
@@ -649,7 +649,7 @@ interface HttpTestHarnessOptions<T = unknown> {
649
649
  };
650
650
  /** Auth provider for generating request headers */
651
651
  auth: AuthProvider;
652
- /** API path prefix (default: '/api') */
652
+ /** API path prefix (default: '/api' for eager, '' for deferred) */
653
653
  apiPrefix?: string;
654
654
  }
655
655
  /** Options can be passed directly or as a getter for deferred resolution */
@@ -666,12 +666,21 @@ type OptionsOrGetter<T> = HttpTestHarnessOptions<T> | (() => HttpTestHarnessOpti
666
666
  declare class HttpTestHarness<T = unknown> {
667
667
  private resource;
668
668
  private optionsOrGetter;
669
- private baseUrl;
669
+ private eagerBaseUrl;
670
670
  private enabledRoutes;
671
671
  private updateMethod;
672
672
  constructor(resource: ResourceDefinition<unknown>, optionsOrGetter: OptionsOrGetter<T>);
673
673
  /** Resolve options (supports both direct and deferred) */
674
674
  private getOptions;
675
+ /**
676
+ * Resolve the base URL for requests.
677
+ *
678
+ * - Eager mode: uses pre-computed baseUrl from constructor
679
+ * - Deferred mode: reads apiPrefix from the getter options at runtime
680
+ *
681
+ * Must only be called inside it()/afterAll() callbacks (after beforeAll has run).
682
+ */
683
+ private getBaseUrl;
675
684
  /**
676
685
  * Run all test suites: CRUD + permissions + validation
677
686
  */
@@ -715,6 +724,7 @@ declare class HttpTestHarness<T = unknown> {
715
724
  *
716
725
  * createHttpTestHarness(jobResource, () => ({
717
726
  * app: ctx.app,
727
+ * apiPrefix: '',
718
728
  * fixtures: { valid: { title: 'Test' } },
719
729
  * auth: createBetterAuthProvider({ ... }),
720
730
  * })).runAll();
@@ -1000,7 +1000,7 @@ var DatabaseSnapshot = class {
1000
1000
  * ```
1001
1001
  */
1002
1002
  async function createTestApp(options = {}) {
1003
- const { createApp } = await import("../createApp-D2D5XXaV.mjs").then((n) => n.r);
1003
+ const { createApp } = await import("../createApp-BTHYuHNU.mjs").then((n) => n.r);
1004
1004
  const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
1005
1005
  const defaultAuth = {
1006
1006
  type: "jwt",
@@ -1606,6 +1606,7 @@ async function setupBetterAuthOrg(options) {
1606
1606
  *
1607
1607
  * const harness = createHttpTestHarness(jobResource, () => ({
1608
1608
  * app: ctx.app,
1609
+ * apiPrefix: '',
1609
1610
  * fixtures: { valid: { title: 'Test' } },
1610
1611
  * auth: createBetterAuthProvider({ tokens: { admin: ctx.users.admin.token }, orgId: ctx.orgId, adminRole: 'admin' }),
1611
1612
  * }));
@@ -1687,13 +1688,14 @@ function createBetterAuthProvider(options) {
1687
1688
  var HttpTestHarness = class {
1688
1689
  resource;
1689
1690
  optionsOrGetter;
1690
- baseUrl;
1691
+ eagerBaseUrl;
1691
1692
  enabledRoutes;
1692
1693
  updateMethod;
1693
1694
  constructor(resource, optionsOrGetter) {
1694
1695
  this.resource = resource;
1695
1696
  this.optionsOrGetter = optionsOrGetter;
1696
- this.baseUrl = `${typeof optionsOrGetter === "function" ? "/api" : optionsOrGetter.apiPrefix ?? "/api"}${resource.prefix}`;
1697
+ if (typeof optionsOrGetter === "function") this.eagerBaseUrl = null;
1698
+ else this.eagerBaseUrl = `${optionsOrGetter.apiPrefix ?? "/api"}${resource.prefix}`;
1697
1699
  const disabled = new Set(resource.disabledRoutes ?? []);
1698
1700
  this.enabledRoutes = new Set(resource.disableDefaultRoutes ? [] : CRUD_OPERATIONS.filter((op) => !disabled.has(op)));
1699
1701
  this.updateMethod = resource.updateMethod === "PUT" ? "PUT" : "PATCH";
@@ -1703,6 +1705,18 @@ var HttpTestHarness = class {
1703
1705
  return typeof this.optionsOrGetter === "function" ? this.optionsOrGetter() : this.optionsOrGetter;
1704
1706
  }
1705
1707
  /**
1708
+ * Resolve the base URL for requests.
1709
+ *
1710
+ * - Eager mode: uses pre-computed baseUrl from constructor
1711
+ * - Deferred mode: reads apiPrefix from the getter options at runtime
1712
+ *
1713
+ * Must only be called inside it()/afterAll() callbacks (after beforeAll has run).
1714
+ */
1715
+ getBaseUrl() {
1716
+ if (this.eagerBaseUrl !== null) return this.eagerBaseUrl;
1717
+ return `${this.getOptions().apiPrefix ?? ""}${this.resource.prefix}`;
1718
+ }
1719
+ /**
1706
1720
  * Run all test suites: CRUD + permissions + validation
1707
1721
  */
1708
1722
  runAll() {
@@ -1722,12 +1736,13 @@ var HttpTestHarness = class {
1722
1736
  * - GET /:id with non-existent ID → 404
1723
1737
  */
1724
1738
  runCrud() {
1725
- const { resource, baseUrl, enabledRoutes, updateMethod } = this;
1739
+ const { resource, enabledRoutes, updateMethod } = this;
1726
1740
  let createdId = null;
1727
1741
  describe(`${resource.displayName} HTTP CRUD`, () => {
1728
1742
  afterAll(async () => {
1729
1743
  if (createdId && enabledRoutes.has("delete")) {
1730
1744
  const { app, auth } = this.getOptions();
1745
+ const baseUrl = this.getBaseUrl();
1731
1746
  await app.inject({
1732
1747
  method: "DELETE",
1733
1748
  url: `${baseUrl}/${createdId}`,
@@ -1737,6 +1752,7 @@ var HttpTestHarness = class {
1737
1752
  });
1738
1753
  if (enabledRoutes.has("create")) it("POST should create a resource", async () => {
1739
1754
  const { app, auth, fixtures } = this.getOptions();
1755
+ const baseUrl = this.getBaseUrl();
1740
1756
  const adminHeaders = auth.getHeaders(auth.adminRole);
1741
1757
  const res = await app.inject({
1742
1758
  method: "POST",
@@ -1753,6 +1769,7 @@ var HttpTestHarness = class {
1753
1769
  });
1754
1770
  if (enabledRoutes.has("list")) it("GET should list resources", async () => {
1755
1771
  const { app, auth } = this.getOptions();
1772
+ const baseUrl = this.getBaseUrl();
1756
1773
  const res = await app.inject({
1757
1774
  method: "GET",
1758
1775
  url: baseUrl,
@@ -1769,6 +1786,7 @@ var HttpTestHarness = class {
1769
1786
  it("GET /:id should return the resource", async () => {
1770
1787
  if (!createdId) return;
1771
1788
  const { app, auth } = this.getOptions();
1789
+ const baseUrl = this.getBaseUrl();
1772
1790
  const res = await app.inject({
1773
1791
  method: "GET",
1774
1792
  url: `${baseUrl}/${createdId}`,
@@ -1782,6 +1800,7 @@ var HttpTestHarness = class {
1782
1800
  });
1783
1801
  it("GET /:id with non-existent ID should return 404", async () => {
1784
1802
  const { app, auth } = this.getOptions();
1803
+ const baseUrl = this.getBaseUrl();
1785
1804
  const res = await app.inject({
1786
1805
  method: "GET",
1787
1806
  url: `${baseUrl}/000000000000000000000000`,
@@ -1795,6 +1814,7 @@ var HttpTestHarness = class {
1795
1814
  it(`${updateMethod} /:id should update the resource`, async () => {
1796
1815
  if (!createdId) return;
1797
1816
  const { app, auth, fixtures } = this.getOptions();
1817
+ const baseUrl = this.getBaseUrl();
1798
1818
  const updatePayload = fixtures.update || fixtures.valid;
1799
1819
  const res = await app.inject({
1800
1820
  method: updateMethod,
@@ -1809,6 +1829,7 @@ var HttpTestHarness = class {
1809
1829
  });
1810
1830
  it(`${updateMethod} /:id with non-existent ID should return 404`, async () => {
1811
1831
  const { app, auth, fixtures } = this.getOptions();
1832
+ const baseUrl = this.getBaseUrl();
1812
1833
  expect((await app.inject({
1813
1834
  method: updateMethod,
1814
1835
  url: `${baseUrl}/000000000000000000000000`,
@@ -1820,6 +1841,7 @@ var HttpTestHarness = class {
1820
1841
  if (enabledRoutes.has("delete")) {
1821
1842
  it("DELETE /:id should delete the resource", async () => {
1822
1843
  const { app, auth, fixtures } = this.getOptions();
1844
+ const baseUrl = this.getBaseUrl();
1823
1845
  const adminHeaders = auth.getHeaders(auth.adminRole);
1824
1846
  let deleteId;
1825
1847
  if (enabledRoutes.has("create")) {
@@ -1845,6 +1867,7 @@ var HttpTestHarness = class {
1845
1867
  });
1846
1868
  it("DELETE /:id with non-existent ID should return 404", async () => {
1847
1869
  const { app, auth } = this.getOptions();
1870
+ const baseUrl = this.getBaseUrl();
1848
1871
  expect((await app.inject({
1849
1872
  method: "DELETE",
1850
1873
  url: `${baseUrl}/000000000000000000000000`,
@@ -1862,10 +1885,11 @@ var HttpTestHarness = class {
1862
1885
  * - Admin role gets 2xx for all operations
1863
1886
  */
1864
1887
  runPermissions() {
1865
- const { resource, baseUrl, enabledRoutes, updateMethod } = this;
1888
+ const { resource, enabledRoutes, updateMethod } = this;
1866
1889
  describe(`${resource.displayName} HTTP Permissions`, () => {
1867
1890
  if (enabledRoutes.has("list")) it("GET list without auth should return 401", async () => {
1868
1891
  const { app } = this.getOptions();
1892
+ const baseUrl = this.getBaseUrl();
1869
1893
  expect((await app.inject({
1870
1894
  method: "GET",
1871
1895
  url: baseUrl
@@ -1873,6 +1897,7 @@ var HttpTestHarness = class {
1873
1897
  });
1874
1898
  if (enabledRoutes.has("get")) it("GET get without auth should return 401", async () => {
1875
1899
  const { app } = this.getOptions();
1900
+ const baseUrl = this.getBaseUrl();
1876
1901
  expect((await app.inject({
1877
1902
  method: "GET",
1878
1903
  url: `${baseUrl}/000000000000000000000000`
@@ -1880,6 +1905,7 @@ var HttpTestHarness = class {
1880
1905
  });
1881
1906
  if (enabledRoutes.has("create")) it("POST create without auth should return 401", async () => {
1882
1907
  const { app, fixtures } = this.getOptions();
1908
+ const baseUrl = this.getBaseUrl();
1883
1909
  expect((await app.inject({
1884
1910
  method: "POST",
1885
1911
  url: baseUrl,
@@ -1888,6 +1914,7 @@ var HttpTestHarness = class {
1888
1914
  });
1889
1915
  if (enabledRoutes.has("update")) it(`${updateMethod} update without auth should return 401`, async () => {
1890
1916
  const { app, fixtures } = this.getOptions();
1917
+ const baseUrl = this.getBaseUrl();
1891
1918
  expect((await app.inject({
1892
1919
  method: updateMethod,
1893
1920
  url: `${baseUrl}/000000000000000000000000`,
@@ -1896,6 +1923,7 @@ var HttpTestHarness = class {
1896
1923
  });
1897
1924
  if (enabledRoutes.has("delete")) it("DELETE delete without auth should return 401", async () => {
1898
1925
  const { app } = this.getOptions();
1926
+ const baseUrl = this.getBaseUrl();
1899
1927
  expect((await app.inject({
1900
1928
  method: "DELETE",
1901
1929
  url: `${baseUrl}/000000000000000000000000`
@@ -1903,6 +1931,7 @@ var HttpTestHarness = class {
1903
1931
  });
1904
1932
  if (enabledRoutes.has("list")) it("admin should access list endpoint", async () => {
1905
1933
  const { app, auth } = this.getOptions();
1934
+ const baseUrl = this.getBaseUrl();
1906
1935
  expect((await app.inject({
1907
1936
  method: "GET",
1908
1937
  url: baseUrl,
@@ -1911,6 +1940,7 @@ var HttpTestHarness = class {
1911
1940
  });
1912
1941
  if (enabledRoutes.has("create")) it("admin should access create endpoint", async () => {
1913
1942
  const { app, auth, fixtures } = this.getOptions();
1943
+ const baseUrl = this.getBaseUrl();
1914
1944
  const res = await app.inject({
1915
1945
  method: "POST",
1916
1946
  url: baseUrl,
@@ -1933,11 +1963,12 @@ var HttpTestHarness = class {
1933
1963
  * Tests that invalid payloads return 400.
1934
1964
  */
1935
1965
  runValidation() {
1936
- const { resource, baseUrl, enabledRoutes } = this;
1966
+ const { resource, enabledRoutes } = this;
1937
1967
  if (!enabledRoutes.has("create")) return;
1938
1968
  describe(`${resource.displayName} HTTP Validation`, () => {
1939
1969
  it("POST with invalid payload should not return 2xx", async () => {
1940
1970
  const { app, auth, fixtures } = this.getOptions();
1971
+ const baseUrl = this.getBaseUrl();
1941
1972
  if (!fixtures.invalid) return;
1942
1973
  const res = await app.inject({
1943
1974
  method: "POST",
@@ -1963,6 +1994,7 @@ var HttpTestHarness = class {
1963
1994
  *
1964
1995
  * createHttpTestHarness(jobResource, () => ({
1965
1996
  * app: ctx.app,
1997
+ * apiPrefix: '',
1966
1998
  * fixtures: { valid: { title: 'Test' } },
1967
1999
  * auth: createBetterAuthProvider({ ... }),
1968
2000
  * })).runAll();
@@ -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-Cb2klgid.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";
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";
@@ -1,5 +1,5 @@
1
1
  import "../elevation-DGo5shaX.mjs";
2
- import "../interface-Cb2klgid.mjs";
2
+ import "../interface-Dm4-jnia.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.1.7",
3
+ "version": "2.2.0",
4
4
  "description": "Resource-oriented backend framework for Fastify — clean, minimal, powerful, tree-shakable",
5
5
  "type": "module",
6
6
  "exports": {
@@ -191,7 +191,7 @@
191
191
  "node": ">=22"
192
192
  },
193
193
  "peerDependencies": {
194
- "@classytic/mongokit": "^3.2.3",
194
+ "@classytic/mongokit": "^3.2.4",
195
195
  "@classytic/streamline": ">=1.0.0",
196
196
  "@fastify/cors": "^11.0.0",
197
197
  "@fastify/helmet": "^13.0.0",