@classytic/arc 1.0.5 → 1.1.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.
- package/README.md +930 -0
- package/dist/BaseController-DVAiHxEQ.d.ts +233 -0
- package/dist/adapters/index.d.ts +2 -2
- package/dist/{arcCorePlugin-Cqi7j5-_.d.ts → arcCorePlugin-CsShQdyP.d.ts} +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/cli/commands/init.js +21 -6
- package/dist/cli/index.js +21 -6
- package/dist/core/index.d.ts +220 -0
- package/dist/core/index.js +2786 -0
- package/dist/{createApp-9q_I1la4.d.ts → createApp-Ce9wl8W9.d.ts} +1 -1
- package/dist/factory/index.d.ts +4 -4
- package/dist/factory/index.js +5 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/{index-WBEvhmWM.d.ts → index-B4t03KQ0.d.ts} +45 -1
- package/dist/index.d.ts +7 -236
- package/dist/index.js +34 -4
- package/dist/org/index.d.ts +1 -1
- package/dist/permissions/index.js +5 -2
- package/dist/plugins/index.d.ts +2 -2
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +3 -1
- package/dist/presets/multiTenant.d.ts +1 -1
- package/dist/registry/index.d.ts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +5 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/{types-EBZBXrg-.d.ts → types-BvckRbs2.d.ts} +1 -1
- package/dist/utils/index.d.ts +28 -4
- package/dist/utils/index.js +34 -8
- package/package.json +10 -8
package/dist/factory/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { A as ArcFactory, c as createApp } from '../createApp-
|
|
2
|
-
import { C as CreateAppOptions } from '../types-
|
|
3
|
-
export { M as MultipartOptions, R as RawBodyOptions, U as UnderPressureOptions } from '../types-
|
|
1
|
+
export { A as ArcFactory, c as createApp } from '../createApp-Ce9wl8W9.js';
|
|
2
|
+
import { C as CreateAppOptions } from '../types-BvckRbs2.js';
|
|
3
|
+
export { M as MultipartOptions, R as RawBodyOptions, U as UnderPressureOptions } from '../types-BvckRbs2.js';
|
|
4
4
|
import 'fastify';
|
|
5
5
|
import '@fastify/cors';
|
|
6
6
|
import '@fastify/helmet';
|
|
7
7
|
import '@fastify/rate-limit';
|
|
8
|
-
import '../index-
|
|
8
|
+
import '../index-B4t03KQ0.js';
|
|
9
9
|
import 'mongoose';
|
|
10
10
|
import '../types-B99TBmFV.js';
|
|
11
11
|
|
package/dist/factory/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import fp from 'fastify-plugin';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import Fastify from 'fastify';
|
|
5
|
+
import qs from 'qs';
|
|
5
6
|
|
|
6
7
|
var __defProp = Object.defineProperty;
|
|
7
8
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1507,6 +1508,10 @@ async function createApp(options) {
|
|
|
1507
1508
|
const fastify = Fastify({
|
|
1508
1509
|
logger: config.logger ?? true,
|
|
1509
1510
|
trustProxy: config.trustProxy ?? false,
|
|
1511
|
+
// Use qs parser to support nested bracket notation in query strings
|
|
1512
|
+
// e.g., ?populate[author][select]=name,email → { populate: { author: { select: 'name,email' } } }
|
|
1513
|
+
// This is required for MongoKit's advanced populate options to work
|
|
1514
|
+
querystringParser: (str) => qs.parse(str),
|
|
1510
1515
|
ajv: {
|
|
1511
1516
|
customOptions: {
|
|
1512
1517
|
coerceTypes: true,
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ac as HookContext, ad as HookHandler,
|
|
1
|
+
export { ac as HookContext, ad as HookHandler, aI as HookOperation, aH as HookPhase, aJ as HookRegistration, H as HookSystem, aK as HookSystemOptions, aC as afterCreate, aG as afterDelete, aE as afterUpdate, aB as beforeCreate, aF as beforeDelete, aD as beforeUpdate, aA as createHookSystem, ab as hookSystem } from '../index-B4t03KQ0.js';
|
|
2
2
|
import 'mongoose';
|
|
3
3
|
import 'fastify';
|
|
4
4
|
import '../types-B99TBmFV.js';
|
|
@@ -553,7 +553,13 @@ interface ControllerQueryOptions {
|
|
|
553
553
|
page?: number;
|
|
554
554
|
limit?: number;
|
|
555
555
|
sort?: string | Record<string, 1 | -1>;
|
|
556
|
+
/** Simple populate (comma-separated string or array) */
|
|
556
557
|
populate?: string | string[] | Record<string, unknown>;
|
|
558
|
+
/**
|
|
559
|
+
* Advanced populate options (Mongoose-compatible)
|
|
560
|
+
* When set, takes precedence over simple `populate`
|
|
561
|
+
*/
|
|
562
|
+
populateOptions?: PopulateOption[];
|
|
557
563
|
select?: string | string[] | Record<string, 0 | 1>;
|
|
558
564
|
filters?: Record<string, unknown>;
|
|
559
565
|
search?: string;
|
|
@@ -565,19 +571,57 @@ interface ControllerQueryOptions {
|
|
|
565
571
|
/** Allow additional options */
|
|
566
572
|
[key: string]: unknown;
|
|
567
573
|
}
|
|
574
|
+
/**
|
|
575
|
+
* Mongoose-compatible populate option for advanced field selection
|
|
576
|
+
* Used when you need to select specific fields from populated documents
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* // URL: ?populate[author][select]=name,email
|
|
581
|
+
* // Generates: { path: 'author', select: 'name email' }
|
|
582
|
+
* ```
|
|
583
|
+
*/
|
|
584
|
+
interface PopulateOption {
|
|
585
|
+
/** Field path to populate */
|
|
586
|
+
path: string;
|
|
587
|
+
/** Fields to select (space-separated) */
|
|
588
|
+
select?: string;
|
|
589
|
+
/** Filter conditions for populated documents */
|
|
590
|
+
match?: Record<string, unknown>;
|
|
591
|
+
/** Query options (limit, sort, skip) */
|
|
592
|
+
options?: {
|
|
593
|
+
limit?: number;
|
|
594
|
+
sort?: Record<string, 1 | -1>;
|
|
595
|
+
skip?: number;
|
|
596
|
+
};
|
|
597
|
+
/** Nested populate configuration */
|
|
598
|
+
populate?: PopulateOption;
|
|
599
|
+
}
|
|
568
600
|
/**
|
|
569
601
|
* Parsed query result from QueryParser
|
|
570
602
|
* Includes pagination, sorting, filtering, etc.
|
|
603
|
+
*
|
|
604
|
+
* The index signature allows custom query parsers (like MongoKit's QueryParser)
|
|
605
|
+
* to add additional fields without breaking Arc's type system.
|
|
571
606
|
*/
|
|
572
607
|
interface ParsedQuery {
|
|
573
608
|
filters?: Record<string, unknown>;
|
|
574
609
|
limit?: number;
|
|
575
610
|
sort?: string | Record<string, 1 | -1>;
|
|
611
|
+
/** Simple populate (comma-separated string or array) */
|
|
576
612
|
populate?: string | string[] | Record<string, unknown>;
|
|
613
|
+
/**
|
|
614
|
+
* Advanced populate options (Mongoose-compatible)
|
|
615
|
+
* When set, takes precedence over simple `populate`
|
|
616
|
+
* @example [{ path: 'author', select: 'name email' }]
|
|
617
|
+
*/
|
|
618
|
+
populateOptions?: PopulateOption[];
|
|
577
619
|
search?: string;
|
|
578
620
|
page?: number;
|
|
579
621
|
after?: string;
|
|
580
622
|
select?: string | string[] | Record<string, 0 | 1>;
|
|
623
|
+
/** Allow additional fields from custom query parsers */
|
|
624
|
+
[key: string]: unknown;
|
|
581
625
|
}
|
|
582
626
|
/**
|
|
583
627
|
* Query Parser Interface
|
|
@@ -1319,4 +1363,4 @@ interface ValidationResult {
|
|
|
1319
1363
|
}
|
|
1320
1364
|
type AdapterFactory<TDoc> = (config: unknown) => DataAdapter<TDoc>;
|
|
1321
1365
|
|
|
1322
|
-
export { type InferDocType as $, type
|
|
1366
|
+
export { type InferDocType as $, type AnyRecord as A, type CrudController as B, type ControllerQueryOptions as C, type DataAdapter as D, type FieldRule as E, type FieldMetadata as F, type CrudSchemas as G, HookSystem as H, type IController as I, type JWTPayload as J, type AdditionalRoute as K, type PresetFunction as L, type MiddlewareConfig as M, type EventDefinition as N, type OwnershipCheck as O, type PaginatedResult as P, type QueryParserInterface as Q, type RouteSchemaOptions as R, type ServiceContext as S, type ResourceMetadata as T, type UserOrganization as U, type ValidationResult as V, type RegistryEntry as W, type RegistryStats as X, type IntrospectionData as Y, type OrgScopeOptions as Z, type CrudRouterOptions as _, type IRequestContext as a, type InferResourceDoc as a0, type TypedResourceConfig as a1, type TypedController as a2, type TypedRepository as a3, type ConfigError as a4, type ValidationResult$1 as a5, type ValidateOptions as a6, type HealthCheck as a7, type HealthOptions as a8, type GracefulShutdownOptions as a9, createHookSystem as aA, beforeCreate as aB, afterCreate as aC, beforeUpdate as aD, afterUpdate as aE, beforeDelete as aF, afterDelete as aG, type HookPhase as aH, type HookOperation as aI, type HookRegistration as aJ, type HookSystemOptions as aK, type RequestIdOptions as aa, hookSystem as ab, type HookContext as ac, type HookHandler as ad, type ParsedQuery as ae, type OpenApiSchemas as af, type AdapterFactory as ag, type ObjectId as ah, type UserLike as ai, getUserId as aj, type PopulateOption as ak, type ArcDecorator as al, type EventsDecorator as am, type ResourcePermissions as an, type ResourceHooks as ao, type MiddlewareHandler as ap, type JwtContext as aq, type AuthenticatorContext as ar, type Authenticator as as, type TokenPair as at, type PresetHook as au, type BaseControllerOptions as av, type PaginationParams as aw, type InferDoc as ax, type ControllerHandler as ay, type FastifyHandler as az, type RequestContext as b, type IControllerResponse as c, type RequestWithExtras as d, type CrudRouteKey as e, type PresetResult as f, type AuthHelpers as g, type AuthPluginOptions as h, type IntrospectionPluginOptions as i, ResourceRegistry as j, type RegisterOptions as k, type ResourceConfig as l, type SchemaMetadata as m, type RelationMetadata as n, type RepositoryLike as o, defineResource as p, ResourceDefinition as q, resourceRegistry as r, type ApiResponse as s, type ControllerLike as t, type FastifyRequestExtras as u, type FastifyWithAuth as v, type FastifyWithDecorators as w, type QueryOptions as x, type CrudRepository as y, type RouteHandler as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,249 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { V as AdapterValidationResult, K as AdditionalRoute, s as ApiResponse,
|
|
1
|
+
import { l as ResourceConfig } from './index-B4t03KQ0.js';
|
|
2
|
+
export { V as AdapterValidationResult, K as AdditionalRoute, A as AnyRecord, s as ApiResponse, h as AuthPluginOptions, a4 as ConfigError, t as ControllerLike, B as CrudController, y as CrudRepository, e as CrudRouteKey, _ as CrudRouterOptions, G as CrudSchemas, D as DataAdapter, N as EventDefinition, u as FastifyRequestExtras, v as FastifyWithAuth, w as FastifyWithDecorators, F as FieldMetadata, E as FieldRule, a9 as GracefulShutdownOptions, a7 as HealthCheck, a8 as HealthOptions, ac as HookContext, ad as HookHandler, H as HookSystem, I as IController, c as IControllerResponse, a as IRequestContext, $ as InferDocType, a0 as InferResourceDoc, Y as IntrospectionData, i as IntrospectionPluginOptions, J as JWTPayload, M as MiddlewareConfig, Z as OrgScopeOptions, O as OwnershipCheck, P as PaginatedResult, L as PresetFunction, f as PresetResult, x as QueryOptions, W as RegistryEntry, X as RegistryStats, n as RelationMetadata, o as RepositoryLike, b as RequestContext, aa as RequestIdOptions, d as RequestWithExtras, q as ResourceDefinition, T as ResourceMetadata, z as RouteHandler, R as RouteSchemaOptions, m as SchemaMetadata, S as ServiceContext, a2 as TypedController, a3 as TypedRepository, a1 as TypedResourceConfig, U as UserOrganization, a6 as ValidateOptions, a5 as ValidationResult, p as defineResource, ab as hookSystem, r as resourceRegistry } from './index-B4t03KQ0.js';
|
|
3
3
|
export { MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, createMongooseAdapter, createPrismaAdapter } from './adapters/index.js';
|
|
4
|
+
export { B as BaseController, a as BaseControllerOptions } from './BaseController-DVAiHxEQ.js';
|
|
4
5
|
export { RouteHandlerMethod } from 'fastify';
|
|
5
6
|
export { P as PermissionCheck, a as PermissionContext, b as PermissionResult, U as UserBase } from './types-B99TBmFV.js';
|
|
6
7
|
export { A as ArcError, F as ForbiddenError, N as NotFoundError, U as UnauthorizedError, V as ValidationError } from './errors-8WIxGS_6.js';
|
|
7
|
-
export { e as gracefulShutdownPlugin, a as healthPlugin, _ as requestIdPlugin } from './arcCorePlugin-
|
|
8
|
+
export { e as gracefulShutdownPlugin, a as healthPlugin, _ as requestIdPlugin } from './arcCorePlugin-CsShQdyP.js';
|
|
8
9
|
export { DomainEvent, EventHandler, eventPlugin } from './events/index.js';
|
|
9
10
|
export { allOf, allowPublic, anyOf, denyAll, requireAuth, requireOwnership, requireRoles, when } from './permissions/index.js';
|
|
10
|
-
export { A as ArcFactory, c as createApp } from './createApp-
|
|
11
|
-
export { C as CreateAppOptions } from './types-
|
|
11
|
+
export { A as ArcFactory, c as createApp } from './createApp-Ce9wl8W9.js';
|
|
12
|
+
export { C as CreateAppOptions } from './types-BvckRbs2.js';
|
|
12
13
|
import 'mongoose';
|
|
13
14
|
import '@fastify/cors';
|
|
14
15
|
import '@fastify/helmet';
|
|
15
16
|
import '@fastify/rate-limit';
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
-
* Base Controller - Framework-Agnostic CRUD Operations
|
|
19
|
-
*
|
|
20
|
-
* Implements IController interface for framework portability.
|
|
21
|
-
* Works with Fastify, Express, Next.js, or any framework via adapter pattern.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* import { BaseController } from '@classytic/arc';
|
|
25
|
-
*
|
|
26
|
-
* // Use Arc's default query parser (works out of the box)
|
|
27
|
-
* class ProductController extends BaseController {
|
|
28
|
-
* constructor(repository: CrudRepository) {
|
|
29
|
-
* super(repository);
|
|
30
|
-
* }
|
|
31
|
-
* }
|
|
32
|
-
*
|
|
33
|
-
* // Or use MongoKit's parser for advanced MongoDB features ($lookup, aggregations)
|
|
34
|
-
* import { QueryParser } from '@classytic/mongokit';
|
|
35
|
-
* defineResource({
|
|
36
|
-
* name: 'product',
|
|
37
|
-
* queryParser: new QueryParser(),
|
|
38
|
-
* // ...
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* // Or use a custom parser for SQL databases
|
|
42
|
-
* defineResource({
|
|
43
|
-
* name: 'user',
|
|
44
|
-
* queryParser: new PgQueryParser(),
|
|
45
|
-
* // ...
|
|
46
|
-
* });
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Flexible repository interface that accepts any repository shape
|
|
51
|
-
* Core CRUD methods use flexible signatures to work with any implementation
|
|
52
|
-
* Custom methods can be added via the index signature
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* // MongoKit repository with custom methods
|
|
56
|
-
* interface MyRepository extends FlexibleRepository {
|
|
57
|
-
* findByEmail(email: string): Promise<User>;
|
|
58
|
-
* customMethod(): Promise<void>;
|
|
59
|
-
* }
|
|
60
|
-
*/
|
|
61
|
-
interface FlexibleRepository {
|
|
62
|
-
getAll(...args: any[]): Promise<any>;
|
|
63
|
-
getById(...args: any[]): Promise<any>;
|
|
64
|
-
create(...args: any[]): Promise<any>;
|
|
65
|
-
update(...args: any[]): Promise<any>;
|
|
66
|
-
delete(...args: any[]): Promise<any>;
|
|
67
|
-
[key: string]: any;
|
|
68
|
-
}
|
|
69
|
-
interface BaseControllerOptions {
|
|
70
|
-
/** Schema options for field sanitization */
|
|
71
|
-
schemaOptions?: RouteSchemaOptions;
|
|
72
|
-
/**
|
|
73
|
-
* Query parser instance
|
|
74
|
-
* Default: Arc built-in query parser (adapter-agnostic).
|
|
75
|
-
* You can swap in MongoKit QueryParser, pgkit parser, etc.
|
|
76
|
-
*/
|
|
77
|
-
queryParser?: QueryParserInterface;
|
|
78
|
-
/** Maximum limit for pagination (default: 100) */
|
|
79
|
-
maxLimit?: number;
|
|
80
|
-
/** Default limit for pagination (default: 20) */
|
|
81
|
-
defaultLimit?: number;
|
|
82
|
-
/** Default sort field (default: '-createdAt') */
|
|
83
|
-
defaultSort?: string;
|
|
84
|
-
/** Resource name for hook execution (e.g., 'product' → 'product.created') */
|
|
85
|
-
resourceName?: string;
|
|
86
|
-
/** Disable automatic event emission (default: false) */
|
|
87
|
-
disableEvents?: boolean;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Framework-agnostic base controller implementing MongoKit's IController
|
|
91
|
-
*
|
|
92
|
-
* @template TDoc - The document type
|
|
93
|
-
* @template TRepository - The repository type (defaults to CrudRepository<TDoc>, preserves custom methods when specified)
|
|
94
|
-
*
|
|
95
|
-
* Use with Fastify adapter for Fastify integration (see createFastifyAdapter in createCrudRouter)
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* // Without custom repository type (backward compatible)
|
|
99
|
-
* class SimpleController extends BaseController<Product> {
|
|
100
|
-
* constructor(repository: CrudRepository<Product>) {
|
|
101
|
-
* super(repository);
|
|
102
|
-
* }
|
|
103
|
-
* }
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* // With custom repository type (type-safe access to custom methods)
|
|
107
|
-
* class ProductController extends BaseController<Product, ProductRepository> {
|
|
108
|
-
* constructor(repository: ProductRepository) {
|
|
109
|
-
* super(repository);
|
|
110
|
-
* }
|
|
111
|
-
*
|
|
112
|
-
* async customMethod(context: IRequestContext) {
|
|
113
|
-
* // TypeScript knows about ProductRepository's custom methods
|
|
114
|
-
* return await this.repository.findByCategory(...);
|
|
115
|
-
* }
|
|
116
|
-
* }
|
|
117
|
-
*/
|
|
118
|
-
declare class BaseController<TDoc = AnyRecord, TRepository extends FlexibleRepository = FlexibleRepository> implements IController<TDoc> {
|
|
119
|
-
protected repository: TRepository;
|
|
120
|
-
protected schemaOptions: RouteSchemaOptions;
|
|
121
|
-
protected queryParser: QueryParserInterface;
|
|
122
|
-
protected maxLimit: number;
|
|
123
|
-
protected defaultLimit: number;
|
|
124
|
-
protected defaultSort: string;
|
|
125
|
-
protected resourceName?: string;
|
|
126
|
-
protected disableEvents: boolean;
|
|
127
|
-
/** Preset field names for dynamic param reading */
|
|
128
|
-
protected _presetFields: {
|
|
129
|
-
slugField?: string;
|
|
130
|
-
parentField?: string;
|
|
131
|
-
};
|
|
132
|
-
constructor(repository: TRepository, options?: BaseControllerOptions);
|
|
133
|
-
/**
|
|
134
|
-
* Inject resource options from defineResource
|
|
135
|
-
*/
|
|
136
|
-
_setResourceOptions(options: {
|
|
137
|
-
schemaOptions?: RouteSchemaOptions;
|
|
138
|
-
presetFields?: {
|
|
139
|
-
slugField?: string;
|
|
140
|
-
parentField?: string;
|
|
141
|
-
};
|
|
142
|
-
resourceName?: string;
|
|
143
|
-
queryParser?: QueryParserInterface;
|
|
144
|
-
}): void;
|
|
145
|
-
/**
|
|
146
|
-
* Build service context from IRequestContext
|
|
147
|
-
*/
|
|
148
|
-
protected _buildContext(req: IRequestContext): ServiceContext;
|
|
149
|
-
/**
|
|
150
|
-
* Parse query into QueryOptions using queryParser
|
|
151
|
-
*/
|
|
152
|
-
protected _parseQueryOptions(req: IRequestContext): ControllerQueryOptions;
|
|
153
|
-
/**
|
|
154
|
-
* Apply org and policy filters
|
|
155
|
-
*/
|
|
156
|
-
protected _applyFilters(options: ControllerQueryOptions, req: IRequestContext): ControllerQueryOptions;
|
|
157
|
-
/**
|
|
158
|
-
* Build filter for single-item operations (get/update/delete)
|
|
159
|
-
* Combines ID filter with policy/org filters for proper security enforcement
|
|
160
|
-
*/
|
|
161
|
-
protected _buildIdFilter(id: string, req: IRequestContext): AnyRecord;
|
|
162
|
-
/**
|
|
163
|
-
* Check if a value matches a MongoDB query operator
|
|
164
|
-
*/
|
|
165
|
-
protected _matchesOperator(itemValue: unknown, operator: string, filterValue: unknown): boolean;
|
|
166
|
-
/**
|
|
167
|
-
* Forbidden paths that could lead to prototype pollution
|
|
168
|
-
*/
|
|
169
|
-
private static readonly FORBIDDEN_PATHS;
|
|
170
|
-
/**
|
|
171
|
-
* Get nested value from object using dot notation (e.g., "owner.id")
|
|
172
|
-
* Security: Validates path against forbidden patterns to prevent prototype pollution
|
|
173
|
-
*/
|
|
174
|
-
protected _getNestedValue(obj: AnyRecord, path: string): unknown;
|
|
175
|
-
/**
|
|
176
|
-
* Check if item matches a single filter condition
|
|
177
|
-
* Supports nested paths (e.g., "owner.id", "metadata.status")
|
|
178
|
-
*/
|
|
179
|
-
protected _matchesFilter(item: AnyRecord, key: string, filterValue: unknown): boolean;
|
|
180
|
-
/**
|
|
181
|
-
* Check if item matches policy filters (for get/update/delete operations)
|
|
182
|
-
* Validates that fetched item satisfies all policy constraints
|
|
183
|
-
* Supports MongoDB query operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex, $and, $or
|
|
184
|
-
*/
|
|
185
|
-
protected _checkPolicyFilters(item: AnyRecord, req: IRequestContext): boolean;
|
|
186
|
-
/** Parse lean option (default: true for performance) */
|
|
187
|
-
protected _parseLean(leanValue: unknown): boolean;
|
|
188
|
-
/** Get blocked fields from schema options */
|
|
189
|
-
protected _getBlockedFields(schemaOptions: RouteSchemaOptions): string[];
|
|
190
|
-
/**
|
|
191
|
-
* Convert parsed select object to string format
|
|
192
|
-
* Converts { name: 1, email: 1, password: 0 } → 'name email -password'
|
|
193
|
-
*/
|
|
194
|
-
protected _selectToString(select: string | string[] | Record<string, 0 | 1> | undefined): string | undefined;
|
|
195
|
-
/** Sanitize select fields */
|
|
196
|
-
protected _sanitizeSelect(select: string | undefined, schemaOptions: RouteSchemaOptions): string | undefined;
|
|
197
|
-
/** Sanitize populate fields */
|
|
198
|
-
protected _sanitizePopulate(populate: unknown, schemaOptions: RouteSchemaOptions): string[] | undefined;
|
|
199
|
-
/** Check org scope for a document */
|
|
200
|
-
protected _checkOrgScope(item: AnyRecord | null, arcContext: RequestContext | undefined): boolean;
|
|
201
|
-
/** Check ownership for update/delete (ownedByUser preset) */
|
|
202
|
-
protected _checkOwnership(item: AnyRecord | null, req: IRequestContext): boolean;
|
|
203
|
-
/**
|
|
204
|
-
* Get hook system from context (instance-scoped) or fall back to global singleton
|
|
205
|
-
* This allows proper isolation when running multiple app instances (e.g., in tests)
|
|
206
|
-
*/
|
|
207
|
-
protected _getHooks(req: IRequestContext): HookSystem;
|
|
208
|
-
/**
|
|
209
|
-
* List resources with filtering, pagination, sorting
|
|
210
|
-
* Implements IController.list()
|
|
211
|
-
*/
|
|
212
|
-
list(req: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
|
|
213
|
-
/**
|
|
214
|
-
* Get single resource by ID
|
|
215
|
-
* Implements IController.get()
|
|
216
|
-
*/
|
|
217
|
-
get(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
218
|
-
/**
|
|
219
|
-
* Create new resource
|
|
220
|
-
* Implements IController.create()
|
|
221
|
-
*/
|
|
222
|
-
create(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
223
|
-
/**
|
|
224
|
-
* Update existing resource
|
|
225
|
-
* Implements IController.update()
|
|
226
|
-
*/
|
|
227
|
-
update(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
228
|
-
/**
|
|
229
|
-
* Delete resource
|
|
230
|
-
* Implements IController.delete()
|
|
231
|
-
*/
|
|
232
|
-
delete(req: IRequestContext): Promise<IControllerResponse<{
|
|
233
|
-
message: string;
|
|
234
|
-
}>>;
|
|
235
|
-
/** Get resource by slug (slugLookup preset) */
|
|
236
|
-
getBySlug(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
237
|
-
/** Get soft-deleted resources (softDelete preset) */
|
|
238
|
-
getDeleted(req: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
|
|
239
|
-
/** Restore soft-deleted resource (softDelete preset) */
|
|
240
|
-
restore(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
241
|
-
/** Get hierarchical tree (tree preset) */
|
|
242
|
-
getTree(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
|
|
243
|
-
/** Get children of parent (tree preset) */
|
|
244
|
-
getChildren(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
18
|
/**
|
|
248
19
|
* Resource Configuration Validator
|
|
249
20
|
*
|
|
@@ -361,4 +132,4 @@ declare function assertValidConfig(config: ResourceConfig, options?: ValidateOpt
|
|
|
361
132
|
|
|
362
133
|
declare const version = "1.0.0";
|
|
363
134
|
|
|
364
|
-
export {
|
|
135
|
+
export { ResourceConfig, assertValidConfig, formatValidationErrors, validateResourceConfig, version };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import fp from 'fastify-plugin';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import Fastify from 'fastify';
|
|
5
|
+
import qs from 'qs';
|
|
5
6
|
|
|
6
7
|
var __defProp = Object.defineProperty;
|
|
7
8
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -2175,6 +2176,23 @@ var ArcQueryParser = class {
|
|
|
2175
2176
|
for (const [key, value] of Object.entries(query)) {
|
|
2176
2177
|
if (reservedKeys.has(key)) continue;
|
|
2177
2178
|
if (value === void 0 || value === null) continue;
|
|
2179
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(key)) continue;
|
|
2180
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
2181
|
+
const operatorObj = value;
|
|
2182
|
+
const operatorKeys = Object.keys(operatorObj);
|
|
2183
|
+
const allOperators = operatorKeys.every((op) => this.operators[op]);
|
|
2184
|
+
if (allOperators && operatorKeys.length > 0) {
|
|
2185
|
+
const mongoFilters = {};
|
|
2186
|
+
for (const [op, opValue] of Object.entries(operatorObj)) {
|
|
2187
|
+
const mongoOp = this.operators[op];
|
|
2188
|
+
if (mongoOp) {
|
|
2189
|
+
mongoFilters[mongoOp] = this.parseFilterValue(opValue, op);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
filters[key] = mongoFilters;
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2178
2196
|
const match = key.match(/^([a-zA-Z_][a-zA-Z0-9_.]*)(?:\[([a-z]+)\])?$/);
|
|
2179
2197
|
if (!match) continue;
|
|
2180
2198
|
const [, fieldName, operator] = match;
|
|
@@ -2316,6 +2334,8 @@ var BaseController = class _BaseController {
|
|
|
2316
2334
|
sort: sortString,
|
|
2317
2335
|
select: this._sanitizeSelect(selectString, this.schemaOptions),
|
|
2318
2336
|
populate: this._sanitizePopulate(parsed.populate, this.schemaOptions),
|
|
2337
|
+
// Advanced populate options from MongoKit QueryParser (takes precedence over simple populate)
|
|
2338
|
+
populateOptions: parsed.populateOptions,
|
|
2319
2339
|
filters: parsed.filters,
|
|
2320
2340
|
// MongoKit features
|
|
2321
2341
|
search: parsed.search,
|
|
@@ -2431,7 +2451,7 @@ var BaseController = class _BaseController {
|
|
|
2431
2451
|
return true;
|
|
2432
2452
|
}
|
|
2433
2453
|
}
|
|
2434
|
-
return itemValue === filterValue;
|
|
2454
|
+
return String(itemValue) === String(filterValue);
|
|
2435
2455
|
}
|
|
2436
2456
|
/**
|
|
2437
2457
|
* Check if item matches policy filters (for get/update/delete operations)
|
|
@@ -2587,7 +2607,10 @@ var BaseController = class _BaseController {
|
|
|
2587
2607
|
const arcContext = req.metadata;
|
|
2588
2608
|
try {
|
|
2589
2609
|
const item = await this.repository.getById(id, options);
|
|
2590
|
-
|
|
2610
|
+
const hasItem = !!item;
|
|
2611
|
+
const orgScopeOk = this._checkOrgScope(item, arcContext);
|
|
2612
|
+
const policyFiltersOk = this._checkPolicyFilters(item, req);
|
|
2613
|
+
if (!hasItem || !orgScopeOk || !policyFiltersOk) {
|
|
2591
2614
|
return {
|
|
2592
2615
|
success: false,
|
|
2593
2616
|
error: "Resource not found",
|
|
@@ -3306,15 +3329,16 @@ function allowPublic() {
|
|
|
3306
3329
|
return check;
|
|
3307
3330
|
}
|
|
3308
3331
|
function requireAuth() {
|
|
3309
|
-
|
|
3332
|
+
const check = (ctx) => {
|
|
3310
3333
|
if (!ctx.user) {
|
|
3311
3334
|
return { granted: false, reason: "Authentication required" };
|
|
3312
3335
|
}
|
|
3313
3336
|
return true;
|
|
3314
3337
|
};
|
|
3338
|
+
return check;
|
|
3315
3339
|
}
|
|
3316
3340
|
function requireRoles(roles, options) {
|
|
3317
|
-
|
|
3341
|
+
const check = (ctx) => {
|
|
3318
3342
|
if (!ctx.user) {
|
|
3319
3343
|
return { granted: false, reason: "Authentication required" };
|
|
3320
3344
|
}
|
|
@@ -3330,6 +3354,8 @@ function requireRoles(roles, options) {
|
|
|
3330
3354
|
reason: `Required roles: ${roles.join(", ")}`
|
|
3331
3355
|
};
|
|
3332
3356
|
};
|
|
3357
|
+
check._roles = roles;
|
|
3358
|
+
return check;
|
|
3333
3359
|
}
|
|
3334
3360
|
function requireOwnership(ownerField = "userId", options) {
|
|
3335
3361
|
return (ctx) => {
|
|
@@ -4583,6 +4609,10 @@ async function createApp(options) {
|
|
|
4583
4609
|
const fastify = Fastify({
|
|
4584
4610
|
logger: config.logger ?? true,
|
|
4585
4611
|
trustProxy: config.trustProxy ?? false,
|
|
4612
|
+
// Use qs parser to support nested bracket notation in query strings
|
|
4613
|
+
// e.g., ?populate[author][select]=name,email → { populate: { author: { select: 'name,email' } } }
|
|
4614
|
+
// This is required for MongoKit's advanced populate options to work
|
|
4615
|
+
querystringParser: (str) => qs.parse(str),
|
|
4586
4616
|
ajv: {
|
|
4587
4617
|
customOptions: {
|
|
4588
4618
|
coerceTypes: true,
|
package/dist/org/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RouteHandlerMethod, FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import {
|
|
2
|
+
import { b as RequestContext, Z as OrgScopeOptions, z as RouteHandler } from '../index-B4t03KQ0.js';
|
|
3
3
|
import { U as UserBase } from '../types-B99TBmFV.js';
|
|
4
4
|
import 'mongoose';
|
|
5
5
|
|
|
@@ -5,15 +5,16 @@ function allowPublic() {
|
|
|
5
5
|
return check;
|
|
6
6
|
}
|
|
7
7
|
function requireAuth() {
|
|
8
|
-
|
|
8
|
+
const check = (ctx) => {
|
|
9
9
|
if (!ctx.user) {
|
|
10
10
|
return { granted: false, reason: "Authentication required" };
|
|
11
11
|
}
|
|
12
12
|
return true;
|
|
13
13
|
};
|
|
14
|
+
return check;
|
|
14
15
|
}
|
|
15
16
|
function requireRoles(roles, options) {
|
|
16
|
-
|
|
17
|
+
const check = (ctx) => {
|
|
17
18
|
if (!ctx.user) {
|
|
18
19
|
return { granted: false, reason: "Authentication required" };
|
|
19
20
|
}
|
|
@@ -29,6 +30,8 @@ function requireRoles(roles, options) {
|
|
|
29
30
|
reason: `Required roles: ${roles.join(", ")}`
|
|
30
31
|
};
|
|
31
32
|
};
|
|
33
|
+
check._roles = roles;
|
|
34
|
+
return check;
|
|
32
35
|
}
|
|
33
36
|
function requireOwnership(ownerField = "userId", options) {
|
|
34
37
|
return (ctx) => {
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { k as ArcCore, A as ArcCorePluginOptions, G as GracefulShutdownOptions, b as HealthCheck, H as HealthOptions, R as RequestIdOptions, T as TracingOptions, f as arcCorePlugin, j as arcCorePluginFn, d as createSpan, e as gracefulShutdownPlugin, g as gracefulShutdownPluginFn, a as healthPlugin, h as healthPluginFn, i as isTracingAvailable, _ as requestIdPlugin, r as requestIdPluginFn, t as traced, c as tracingPlugin } from '../arcCorePlugin-
|
|
1
|
+
export { k as ArcCore, A as ArcCorePluginOptions, G as GracefulShutdownOptions, b as HealthCheck, H as HealthOptions, R as RequestIdOptions, T as TracingOptions, f as arcCorePlugin, j as arcCorePluginFn, d as createSpan, e as gracefulShutdownPlugin, g as gracefulShutdownPluginFn, a as healthPlugin, h as healthPluginFn, i as isTracingAvailable, _ as requestIdPlugin, r as requestIdPluginFn, t as traced, c as tracingPlugin } from '../arcCorePlugin-CsShQdyP.js';
|
|
2
2
|
import { FastifyInstance, FastifyRequest } from 'fastify';
|
|
3
|
-
import '../index-
|
|
3
|
+
import '../index-B4t03KQ0.js';
|
|
4
4
|
import 'mongoose';
|
|
5
5
|
import '../types-B99TBmFV.js';
|
|
6
6
|
|
package/dist/presets/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MultiTenantOptions } from './multiTenant.js';
|
|
2
2
|
export { default as multiTenantPreset } from './multiTenant.js';
|
|
3
|
-
import {
|
|
3
|
+
import { f as PresetResult, a as IRequestContext, c as IControllerResponse, P as PaginatedResult, A as AnyRecord, l as ResourceConfig } from '../index-B4t03KQ0.js';
|
|
4
4
|
import 'mongoose';
|
|
5
5
|
import 'fastify';
|
|
6
6
|
import '../types-B99TBmFV.js';
|
package/dist/presets/index.js
CHANGED
|
@@ -5,7 +5,7 @@ function allowPublic() {
|
|
|
5
5
|
return check;
|
|
6
6
|
}
|
|
7
7
|
function requireRoles(roles, options) {
|
|
8
|
-
|
|
8
|
+
const check = (ctx) => {
|
|
9
9
|
if (!ctx.user) {
|
|
10
10
|
return { granted: false, reason: "Authentication required" };
|
|
11
11
|
}
|
|
@@ -18,6 +18,8 @@ function requireRoles(roles, options) {
|
|
|
18
18
|
reason: `Required roles: ${roles.join(", ")}`
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
|
+
check._roles = roles;
|
|
22
|
+
return check;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// src/presets/softDelete.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as RequestWithExtras, e as CrudRouteKey, f as PresetResult } from '../index-B4t03KQ0.js';
|
|
2
2
|
import 'mongoose';
|
|
3
3
|
import 'fastify';
|
|
4
4
|
import '../types-B99TBmFV.js';
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { i as IntrospectionPluginOptions } from '../index-B4t03KQ0.js';
|
|
2
|
+
export { k as RegisterOptions, j as ResourceRegistry, r as resourceRegistry } from '../index-B4t03KQ0.js';
|
|
3
3
|
import { FastifyPluginAsync } from 'fastify';
|
|
4
4
|
import 'mongoose';
|
|
5
5
|
import '../types-B99TBmFV.js';
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { q as ResourceDefinition,
|
|
1
|
+
import { q as ResourceDefinition, A as AnyRecord, y as CrudRepository } from '../index-B4t03KQ0.js';
|
|
2
2
|
import Fastify, { FastifyInstance } from 'fastify';
|
|
3
|
-
import { C as CreateAppOptions } from '../types-
|
|
3
|
+
import { C as CreateAppOptions } from '../types-BvckRbs2.js';
|
|
4
4
|
import { Mock } from 'vitest';
|
|
5
5
|
import { Connection } from 'mongoose';
|
|
6
6
|
import '../types-B99TBmFV.js';
|
package/dist/testing/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import fp from 'fastify-plugin';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import Fastify from 'fastify';
|
|
5
|
+
import qs from 'qs';
|
|
5
6
|
import { describe, beforeAll, afterAll, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
6
7
|
import mongoose from 'mongoose';
|
|
7
8
|
|
|
@@ -46964,6 +46965,10 @@ async function createApp(options2) {
|
|
|
46964
46965
|
const fastify = Fastify({
|
|
46965
46966
|
logger: config.logger ?? true,
|
|
46966
46967
|
trustProxy: config.trustProxy ?? false,
|
|
46968
|
+
// Use qs parser to support nested bracket notation in query strings
|
|
46969
|
+
// e.g., ?populate[author][select]=name,email → { populate: { author: { select: 'name,email' } } }
|
|
46970
|
+
// This is required for MongoKit's advanced populate options to work
|
|
46971
|
+
querystringParser: (str) => qs.parse(str),
|
|
46967
46972
|
ajv: {
|
|
46968
46973
|
customOptions: {
|
|
46969
46974
|
coerceTypes: true,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { K as AdditionalRoute,
|
|
1
|
+
export { K as AdditionalRoute, A as AnyRecord, s as ApiResponse, al as ArcDecorator, g as AuthHelpers, h as AuthPluginOptions, as as Authenticator, ar as AuthenticatorContext, av as BaseControllerOptions, a4 as ConfigError, ay as ControllerHandler, t as ControllerLike, C as ControllerQueryOptions, B as CrudController, y as CrudRepository, e as CrudRouteKey, _ as CrudRouterOptions, G as CrudSchemas, N as EventDefinition, am as EventsDecorator, az as FastifyHandler, u as FastifyRequestExtras, v as FastifyWithAuth, w as FastifyWithDecorators, E as FieldRule, a9 as GracefulShutdownOptions, a7 as HealthCheck, a8 as HealthOptions, I as IController, c as IControllerResponse, a as IRequestContext, ax as InferDoc, $ as InferDocType, a0 as InferResourceDoc, Y as IntrospectionData, i as IntrospectionPluginOptions, J as JWTPayload, aq as JwtContext, M as MiddlewareConfig, ap as MiddlewareHandler, ah as ObjectId, af as OpenApiSchemas, Z as OrgScopeOptions, O as OwnershipCheck, P as PaginatedResult, aw as PaginationParams, ae as ParsedQuery, ak as PopulateOption, L as PresetFunction, au as PresetHook, f as PresetResult, x as QueryOptions, Q as QueryParserInterface, W as RegistryEntry, X as RegistryStats, b as RequestContext, aa as RequestIdOptions, d as RequestWithExtras, l as ResourceConfig, ao as ResourceHooks, T as ResourceMetadata, an as ResourcePermissions, z as RouteHandler, R as RouteSchemaOptions, S as ServiceContext, at as TokenPair, a2 as TypedController, a3 as TypedRepository, a1 as TypedResourceConfig, ai as UserLike, U as UserOrganization, a6 as ValidateOptions, a5 as ValidationResult, aj as getUserId } from '../index-B4t03KQ0.js';
|
|
2
2
|
export { RouteHandlerMethod } from 'fastify';
|
|
3
3
|
export { Document, Model } from 'mongoose';
|
|
4
4
|
export { P as PermissionCheck, a as PermissionContext, b as PermissionResult, U as UserBase } from '../types-B99TBmFV.js';
|
|
@@ -2,7 +2,7 @@ import { FastifyServerOptions } from 'fastify';
|
|
|
2
2
|
import { FastifyCorsOptions } from '@fastify/cors';
|
|
3
3
|
import { FastifyHelmetOptions } from '@fastify/helmet';
|
|
4
4
|
import { RateLimitOptions } from '@fastify/rate-limit';
|
|
5
|
-
import {
|
|
5
|
+
import { h as AuthPluginOptions } from './index-B4t03KQ0.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Types for createApp factory
|