@classytic/arc 2.1.7 → 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.
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/mongodb.d.mts +1 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/cli/commands/init.mjs +11 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/{createApp-D2D5XXaV.mjs → createApp-BKHSl2nT.mjs} +33 -4
- package/dist/{defineResource-DZVbwsFb.mjs → defineResource-DO9ONe_D.mjs} +49 -12
- package/dist/docs/index.d.mts +1 -1
- package/dist/factory/index.d.mts +2 -2
- package/dist/factory/index.mjs +1 -1
- package/dist/{fastifyAdapter-sGkvUvf5.d.mts → fastifyAdapter-CyAA2zlB.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{interface-Cb2klgid.d.mts → interface-DZYNK9bb.d.mts} +14 -0
- package/dist/org/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/{prisma-DQBSSHAB.d.mts → prisma-xjhMEq_S.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/testing/index.d.mts +14 -4
- package/dist/testing/index.mjs +38 -6
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-B0dhNrnd.d.mts → types-DMSBMkaZ.d.mts} +7 -0
- package/dist/utils/index.d.mts +1 -1
- package/package.json +2 -2
|
@@ -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-
|
|
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-
|
|
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 };
|
package/dist/audit/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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";
|
package/dist/audit/mongodb.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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 };
|
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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";
|
|
@@ -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
|
}
|
package/dist/core/index.d.mts
CHANGED
|
@@ -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-
|
|
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-
|
|
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 };
|
package/dist/core/index.mjs
CHANGED
|
@@ -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-
|
|
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 };
|
|
@@ -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");
|
|
@@ -474,10 +479,34 @@ async function createApp(options) {
|
|
|
474
479
|
fastify.log.debug("Custom authentication plugin enabled");
|
|
475
480
|
break;
|
|
476
481
|
case "authenticator": {
|
|
477
|
-
const { authenticate } = authConfig;
|
|
482
|
+
const { authenticate, optionalAuthenticate } = authConfig;
|
|
478
483
|
fastify.decorate("authenticate", async function(request, reply) {
|
|
479
484
|
await authenticate(request, reply);
|
|
480
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
|
+
});
|
|
481
510
|
trackPlugin("auth-authenticator");
|
|
482
511
|
fastify.log.debug("Custom authenticator enabled");
|
|
483
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))
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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;
|
|
@@ -397,6 +413,16 @@ var BaseController = class {
|
|
|
397
413
|
this.update = this.update.bind(this);
|
|
398
414
|
this.delete = this.delete.bind(this);
|
|
399
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Get the tenant field name if multi-tenant scoping is enabled.
|
|
418
|
+
* Returns `undefined` when `tenantField` is `false` (platform-universal mode).
|
|
419
|
+
*
|
|
420
|
+
* Use this in subclass overrides instead of accessing `this.tenantField` directly
|
|
421
|
+
* to avoid TypeScript indexing errors with `string | false`.
|
|
422
|
+
*/
|
|
423
|
+
getTenantField() {
|
|
424
|
+
return this.tenantField || void 0;
|
|
425
|
+
}
|
|
400
426
|
/** Extract typed Arc internal metadata from request */
|
|
401
427
|
meta(req) {
|
|
402
428
|
return req.metadata;
|
|
@@ -772,9 +798,8 @@ var BaseController = class {
|
|
|
772
798
|
const slugField = this._presetFields.slugField ?? "slug";
|
|
773
799
|
const slug = req.params[slugField] ?? req.params.slug;
|
|
774
800
|
const options = this.queryResolver.resolve(req, this.meta(req));
|
|
775
|
-
const arcContext = this.meta(req);
|
|
776
801
|
const item = await repo.getBySlug(slug, options);
|
|
777
|
-
if (!
|
|
802
|
+
if (!this.accessControl.validateItemAccess(item, req)) return {
|
|
778
803
|
success: false,
|
|
779
804
|
error: "Resource not found",
|
|
780
805
|
status: 404
|
|
@@ -826,6 +851,18 @@ var BaseController = class {
|
|
|
826
851
|
error: "ID parameter is required",
|
|
827
852
|
status: 400
|
|
828
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
|
+
};
|
|
829
866
|
const item = await repo.restore(id);
|
|
830
867
|
if (!item) return {
|
|
831
868
|
success: false,
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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";
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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-
|
|
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
|
package/dist/factory/index.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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";
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
/**
|
|
@@ -978,6 +984,14 @@ declare class BaseController<TDoc = AnyRecord, TRepository extends RepositoryLik
|
|
|
978
984
|
private _presetFields;
|
|
979
985
|
private _cacheConfig?;
|
|
980
986
|
constructor(repository: TRepository, options?: BaseControllerOptions);
|
|
987
|
+
/**
|
|
988
|
+
* Get the tenant field name if multi-tenant scoping is enabled.
|
|
989
|
+
* Returns `undefined` when `tenantField` is `false` (platform-universal mode).
|
|
990
|
+
*
|
|
991
|
+
* Use this in subclass overrides instead of accessing `this.tenantField` directly
|
|
992
|
+
* to avoid TypeScript indexing errors with `string | false`.
|
|
993
|
+
*/
|
|
994
|
+
protected getTenantField(): string | undefined;
|
|
981
995
|
/** Extract typed Arc internal metadata from request */
|
|
982
996
|
private meta;
|
|
983
997
|
/** Get hook system from request context (instance-scoped) */
|
package/dist/org/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import { S as RouteHandler } from "../interface-
|
|
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";
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import { K as HookSystem, w as ResourceRegistry } from "../interface-
|
|
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";
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import { b as IControllerResponse, k as PaginatedResult, x as IRequestContext } from "../interface-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { D as CrudRepository, a as RepositoryLike, n as DataAdapter, o as SchemaMetadata, s as ValidationResult } from "./interface-
|
|
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-
|
|
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";
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import { D as CrudRepository, T as ResourceDefinition } from "../interface-
|
|
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-
|
|
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";
|
|
@@ -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
|
|
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();
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1000,7 +1000,7 @@ var DatabaseSnapshot = class {
|
|
|
1000
1000
|
* ```
|
|
1001
1001
|
*/
|
|
1002
1002
|
async function createTestApp(options = {}) {
|
|
1003
|
-
const { createApp } = await import("../createApp-
|
|
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",
|
|
@@ -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
|
-
|
|
1691
|
+
eagerBaseUrl;
|
|
1691
1692
|
enabledRoutes;
|
|
1692
1693
|
updateMethod;
|
|
1693
1694
|
constructor(resource, optionsOrGetter) {
|
|
1694
1695
|
this.resource = resource;
|
|
1695
1696
|
this.optionsOrGetter = optionsOrGetter;
|
|
1696
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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();
|
package/dist/types/index.d.mts
CHANGED
|
@@ -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-
|
|
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
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../elevation-DGo5shaX.mjs";
|
|
2
|
-
import "../interface-
|
|
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.
|
|
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": {
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
"node": ">=22"
|
|
192
192
|
},
|
|
193
193
|
"peerDependencies": {
|
|
194
|
-
"@classytic/mongokit": "^3.2.
|
|
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",
|