@classytic/arc 2.8.5 → 2.10.3
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 +50 -38
- package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-CbKKIflT.mjs} +193 -143
- package/dist/EventTransport-CUw5NNWe.d.mts +293 -0
- package/dist/{ResourceRegistry-C6uXlWe3.mjs → ResourceRegistry-BPd6NQDm.mjs} +1 -1
- package/dist/adapters/index.d.mts +3 -3
- package/dist/adapters/index.mjs +2 -2
- package/dist/{adapters-BBqAVvPK.mjs → adapters-BXY4i-hw.mjs} +210 -41
- package/dist/audit/index.d.mts +135 -11
- package/dist/audit/index.mjs +107 -20
- package/dist/auth/index.d.mts +17 -9
- package/dist/auth/index.mjs +14 -7
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-BuUcUEJq.mjs → betterAuthOpenApi-BBRVhjQN.mjs} +1 -1
- package/dist/cache/index.d.mts +17 -15
- package/dist/cache/index.mjs +15 -14
- package/dist/{caching-IMuYVjTL.mjs → caching-CBpK_SCM.mjs} +8 -3
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -6
- package/dist/{defineResource-tcgySDo1.mjs → core-CcR01lup.mjs} +58 -61
- package/dist/{createActionRouter-BORM8f17.mjs → createActionRouter-Bp_5c_2b.mjs} +3 -3
- package/dist/{createApp-B1EY8zxa.mjs → createApp-BuvPma24.mjs} +15 -14
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DtFxrG0s.mjs → elevation-C7hgL_aI.mjs} +22 -8
- package/dist/{errorHandler-f869_8PQ.mjs → errorHandler-Bb49BvPD.mjs} +59 -7
- package/dist/{errorHandler-Bah5JhBd.d.mts → errorHandler-DRQ3EqfL.d.mts} +37 -2
- package/dist/{eventPlugin-D9DKB2zM.d.mts → eventPlugin-CxWgpd6K.d.mts} +14 -2
- package/dist/{eventPlugin-CDjVTM82.mjs → eventPlugin-DCUjuiQT.mjs} +83 -5
- package/dist/events/index.d.mts +150 -36
- package/dist/events/index.mjs +355 -101
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +2 -2
- package/dist/{types-DZi1aYhm.d.mts → fields-Lo1VUDpt.d.mts} +121 -1
- package/dist/{fields-ipsbIRPK.mjs → fields-bxkeltzz.mjs} +18 -5
- package/dist/{filesUpload-C7r7HIeA.mjs → filesUpload-t21LS-py.mjs} +65 -7
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +32 -5
- package/dist/idempotency/index.mjs +119 -12
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-DtDzOBn8.d.mts → index-8qw4y6ff.d.mts} +4 -135
- package/dist/{index-BLXBmWud.d.mts → index-ChIw3776.d.mts} +283 -408
- package/dist/{interface-CMRutPfe.d.mts → index-Cl0uoKd5.d.mts} +1758 -2506
- package/dist/{index-C1meYuDn.d.mts → index-DStwgFUK.d.mts} +81 -7
- package/dist/index.d.mts +7 -8
- package/dist/index.mjs +11 -12
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +26 -8
- package/dist/integrations/mcp/index.mjs +96 -17
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +5 -0
- package/dist/integrations/webhooks.mjs +6 -0
- package/dist/interface-D218ikEo.d.mts +77 -0
- package/dist/{memory-Cp7_cAko.mjs → memory-B5Amv9A1.mjs} +23 -8
- package/dist/{openapi-CbKUJY_m.mjs → openapi-B5F8AddX.mjs} +3 -3
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -4
- package/dist/permissions/index.mjs +5 -5
- package/dist/{permissions-CH4cNwJi.mjs → permissions-Dk6mshja.mjs} +315 -397
- package/dist/plugins/index.d.mts +7 -7
- package/dist/plugins/index.mjs +14 -16
- package/dist/plugins/response-cache.mjs +2 -2
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +27 -5
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +4 -3
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +2 -2
- package/dist/presets/search.d.mts +178 -0
- package/dist/presets/search.mjs +150 -0
- package/dist/{presets-C2xgzW6x.mjs → presets-fLJVXdVn.mjs} +1 -1
- package/dist/{queryCachePlugin-BJJGBTlu.d.mts → queryCachePlugin-BKbWjgDG.d.mts} +1 -1
- package/dist/{queryCachePlugin-BH-fidlv.mjs → queryCachePlugin-DQCEfJis.mjs} +9 -9
- package/dist/{queryParser-CgCtsjti.mjs → queryParser-DBqBB6AC.mjs} +1 -1
- package/dist/{redis-BM00zaPB.d.mts → redis-DqyeggCa.d.mts} +1 -1
- package/dist/{redis-stream-CrsfUmPt.d.mts → redis-stream-CakIQmwR.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{resourceToTools-8s-EsCCe.mjs → resourceToTools-BElv3xPT.mjs} +65 -48
- package/dist/{schemaConverter-Y7nCYaLJ.mjs → schemaConverter-BxFDdtXu.mjs} +1 -1
- package/dist/scope/index.d.mts +1 -1
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-Ad7ypl9e.mjs → sse-yBCgOLGu.mjs} +1 -1
- package/dist/store-helpers-ZCSMJJAX.mjs +57 -0
- package/dist/testing/index.d.mts +9 -17
- package/dist/testing/index.mjs +27 -83
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -31
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-BsbNMEDR.d.mts → types-Btdda02s.d.mts} +1 -1
- package/dist/{types-Ch9pTQbf.d.mts → types-Co8k3NyS.d.mts} +11 -9
- package/dist/types-Csi3FLfq.mjs +27 -0
- package/dist/utils/index.d.mts +208 -4
- package/dist/utils/index.mjs +5 -6
- package/dist/{utils-yYT3HDXt.mjs → utils-B2fNOD_i.mjs} +285 -2
- package/dist/{versioning-CDugduqI.mjs → versioning-C2U_bLY0.mjs} +3 -5
- package/package.json +20 -26
- package/skills/arc/SKILL.md +97 -23
- package/skills/arc/references/auth.md +94 -0
- package/skills/arc/references/events.md +200 -12
- package/skills/arc/references/mcp.md +4 -17
- package/skills/arc/references/multi-tenancy.md +43 -0
- package/skills/arc/references/production.md +34 -60
- package/dist/EventTransport-BXja8NOc.d.mts +0 -135
- package/dist/audit/mongodb.d.mts +0 -2
- package/dist/audit/mongodb.mjs +0 -2
- package/dist/circuitBreaker-cmi5XDv5.mjs +0 -284
- package/dist/circuitBreaker-dTtG-UyS.d.mts +0 -206
- package/dist/core-F0QoWBt2.mjs +0 -34
- package/dist/dynamic/index.d.mts +0 -93
- package/dist/dynamic/index.mjs +0 -122
- package/dist/fields-DpZQa_Q3.d.mts +0 -109
- package/dist/idempotency/mongodb.d.mts +0 -2
- package/dist/idempotency/mongodb.mjs +0 -123
- package/dist/interface-4y979v99.d.mts +0 -54
- package/dist/mongodb-BsP-WbhN.d.mts +0 -127
- package/dist/mongodb-CTcp0hQZ.d.mts +0 -80
- package/dist/mongodb-Utc5k_-0.mjs +0 -90
- package/dist/policies/index.d.mts +0 -432
- package/dist/policies/index.mjs +0 -318
- package/dist/rpc/index.d.mts +0 -90
- package/dist/rpc/index.mjs +0 -248
- /package/dist/{HookSystem-HprTmvVY.mjs → HookSystem-BNYKnrXF.mjs} +0 -0
- /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
- /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
- /package/dist/{elevation-B6S5csVA.d.mts → elevation-C5SwtkAn.d.mts} +0 -0
- /package/dist/{errors-Ck2h67pm.d.mts → errors-CCSsMpXE.d.mts} +0 -0
- /package/dist/{errors-BF2bIOIS.mjs → errors-D5c-5BJL.mjs} +0 -0
- /package/dist/{externalPaths-BnkYrNzp.d.mts → externalPaths-BQ8QijNH.d.mts} +0 -0
- /package/dist/{interface-DfLGcus7.d.mts → interface-CSbZdv_3.d.mts} +0 -0
- /package/dist/{loadResources-PWd0OCpV.mjs → loadResources-BAzJItAJ.mjs} +0 -0
- /package/dist/{logger-D1YrIImS.mjs → logger-DLg8-Ueg.mjs} +0 -0
- /package/dist/{metrics-B-PU4-Yu.mjs → metrics-DuhiSEZI.mjs} +0 -0
- /package/dist/{pluralize-CWP6MB39.mjs → pluralize-A0tWEl1K.mjs} +0 -0
- /package/dist/{registry-BiTKT1Dg.mjs → registry-B3lRFBWo.mjs} +0 -0
- /package/dist/{replyHelpers-CxkYGT81.mjs → replyHelpers-CXtJDAZ0.mjs} +0 -0
- /package/dist/{requestContext-DYvHl113.mjs → requestContext-xHIKedG6.mjs} +0 -0
- /package/dist/{sessionManager-DDCmiNIo.d.mts → sessionManager-BkzVU8h2.d.mts} +0 -0
- /package/dist/{storage-Dfzt4VTl.d.mts → storage-CVk_SEn2.d.mts} +0 -0
- /package/dist/{tracing-DdN2-wHJ.d.mts → tracing-65B51Dw3.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
- /package/dist/{types-ZUu_h0jp.mjs → types-DV9WDfeg.mjs} +0 -0
|
@@ -1,6 +1,80 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as SchemaMetadata, Et as DataAdapter, Kt as QueryParserInterface, Tt as AdapterSchemaContext, Wt as ParsedQuery, gt as RouteSchemaOptions, jt as ValidationResult, kt as RepositoryLike, rt as OpenApiSchemas } from "./index-Cl0uoKd5.mjs";
|
|
2
2
|
import { Model } from "mongoose";
|
|
3
|
+
import { StandardRepo } from "@classytic/repo-core/repository";
|
|
3
4
|
|
|
5
|
+
//#region src/adapters/drizzle.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Minimum shape arc needs from a Drizzle column. Every SQLite, PG, and MySQL
|
|
8
|
+
* column in `drizzle-orm` exposes these via `getTableColumns(table)`. Held
|
|
9
|
+
* structurally so arc doesn't depend on `drizzle-orm` types.
|
|
10
|
+
*/
|
|
11
|
+
interface DrizzleColumnLike {
|
|
12
|
+
columnType?: string;
|
|
13
|
+
dataType?: "number" | "string" | "date" | "boolean" | "json" | "buffer" | "bigint" | "custom";
|
|
14
|
+
notNull?: boolean;
|
|
15
|
+
hasDefault?: boolean;
|
|
16
|
+
primary?: boolean;
|
|
17
|
+
enumValues?: readonly string[];
|
|
18
|
+
length?: number;
|
|
19
|
+
name?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Structural Drizzle table — only requires `[Symbol.for('drizzle:Columns')]`,
|
|
23
|
+
* which every Drizzle table exposes. Matches `drizzle-orm`'s `Table` at
|
|
24
|
+
* runtime without importing it at compile time.
|
|
25
|
+
*/
|
|
26
|
+
type DrizzleTableLike = Record<symbol, Record<string, DrizzleColumnLike>> & {
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
interface DrizzleAdapterOptions<TDoc = unknown> {
|
|
30
|
+
/** Drizzle table — used for schema introspection. */
|
|
31
|
+
table: DrizzleTableLike;
|
|
32
|
+
/** Repository implementing the repo-core contract. */
|
|
33
|
+
repository: StandardRepo<TDoc> | RepositoryLike<TDoc>;
|
|
34
|
+
/**
|
|
35
|
+
* External schema generator. When provided, replaces the built-in
|
|
36
|
+
* type-only conversion. Wire it to your kit's `buildCrudSchemasFromTable`
|
|
37
|
+
* (sqlitekit, pgkit, ...) to get the full CRUD schemas — strict
|
|
38
|
+
* additional-property control, field-rule application, param-type
|
|
39
|
+
* narrowing from primary-key columns, etc.
|
|
40
|
+
*/
|
|
41
|
+
schemaGenerator?: (table: DrizzleTableLike, options?: RouteSchemaOptions, context?: AdapterSchemaContext) => OpenApiSchemas | Record<string, unknown>;
|
|
42
|
+
/** Optional name — defaults to "DrizzleAdapter". */
|
|
43
|
+
name?: string;
|
|
44
|
+
}
|
|
45
|
+
declare class DrizzleAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
46
|
+
readonly type: "drizzle";
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly table: DrizzleTableLike;
|
|
49
|
+
readonly repository: StandardRepo<TDoc> | RepositoryLike<TDoc>;
|
|
50
|
+
private readonly schemaGenerator?;
|
|
51
|
+
constructor(options: DrizzleAdapterOptions<TDoc>);
|
|
52
|
+
/**
|
|
53
|
+
* Introspect Drizzle columns into arc's schema metadata shape.
|
|
54
|
+
*/
|
|
55
|
+
getSchemaMetadata(): SchemaMetadata;
|
|
56
|
+
/**
|
|
57
|
+
* Generate OpenAPI schemas. Delegates to the user-provided
|
|
58
|
+
* `schemaGenerator` when available (strongly recommended — that's where
|
|
59
|
+
* field rules, omit lists, and param-type narrowing live). The built-in
|
|
60
|
+
* fallback emits a permissive entity + CRUD body shape so routes still
|
|
61
|
+
* register when no generator is provided.
|
|
62
|
+
*
|
|
63
|
+
* After the kit generator runs, arc merges constraint-style field rules
|
|
64
|
+
* (`minLength`, `maxLength`, `min`, `max`, `pattern`, `enum`, `description`)
|
|
65
|
+
* into the resulting property schemas so sqlitekit / pgkit behave
|
|
66
|
+
* identically to mongoose here — rule-driven AJV constraints apply
|
|
67
|
+
* regardless of backend.
|
|
68
|
+
*/
|
|
69
|
+
generateSchemas(schemaOptions?: RouteSchemaOptions, context?: AdapterSchemaContext): OpenApiSchemas | Record<string, unknown> | null;
|
|
70
|
+
healthCheck(): Promise<boolean>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Factory — preferred construction style for symmetry with
|
|
74
|
+
* `createMongooseAdapter` / `createPrismaAdapter`.
|
|
75
|
+
*/
|
|
76
|
+
declare function createDrizzleAdapter<TDoc = unknown>(options: DrizzleAdapterOptions<TDoc>): DrizzleAdapter<TDoc>;
|
|
77
|
+
//#endregion
|
|
4
78
|
//#region src/adapters/mongoose.d.ts
|
|
5
79
|
/**
|
|
6
80
|
* Options for creating a Mongoose adapter.
|
|
@@ -12,7 +86,7 @@ interface MongooseAdapterOptions<TDoc = unknown> {
|
|
|
12
86
|
/** Mongoose model instance — preserves document type for type safety */
|
|
13
87
|
model: Model<TDoc>;
|
|
14
88
|
/** Repository implementing CRUD operations - accepts any repository-like object */
|
|
15
|
-
repository:
|
|
89
|
+
repository: StandardRepo<TDoc> | RepositoryLike<TDoc>;
|
|
16
90
|
/**
|
|
17
91
|
* External schema generator plugin for OpenAPI docs.
|
|
18
92
|
* When provided, replaces the built-in basic type conversion.
|
|
@@ -40,7 +114,7 @@ declare class MongooseAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
|
40
114
|
readonly type: "mongoose";
|
|
41
115
|
readonly name: string;
|
|
42
116
|
readonly model: Model<TDoc>;
|
|
43
|
-
readonly repository:
|
|
117
|
+
readonly repository: StandardRepo<TDoc> | RepositoryLike<TDoc>;
|
|
44
118
|
private readonly schemaGenerator?;
|
|
45
119
|
constructor(options: MongooseAdapterOptions<TDoc>);
|
|
46
120
|
/**
|
|
@@ -79,7 +153,7 @@ declare class MongooseAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
|
79
153
|
* const adapter = createMongooseAdapter(ProductModel, productRepository);
|
|
80
154
|
* ```
|
|
81
155
|
*/
|
|
82
|
-
declare function createMongooseAdapter<TDoc = unknown>(model: Model<TDoc>, repository:
|
|
156
|
+
declare function createMongooseAdapter<TDoc = unknown>(model: Model<TDoc>, repository: StandardRepo<TDoc> | RepositoryLike<TDoc>): DataAdapter<TDoc>;
|
|
83
157
|
declare function createMongooseAdapter<TDoc = unknown>(options: MongooseAdapterOptions<TDoc>): DataAdapter<TDoc>;
|
|
84
158
|
//#endregion
|
|
85
159
|
//#region src/adapters/prisma.d.ts
|
|
@@ -205,7 +279,7 @@ interface PrismaAdapterOptions<TModel> {
|
|
|
205
279
|
/** Model name (e.g., 'user', 'product') */
|
|
206
280
|
modelName: string;
|
|
207
281
|
/** Repository instance implementing CRUD operations */
|
|
208
|
-
repository:
|
|
282
|
+
repository: StandardRepo<TModel>;
|
|
209
283
|
/** Optional: Prisma DMMF (Data Model Meta Format) for schema extraction */
|
|
210
284
|
dmmf?: PrismaDmmf;
|
|
211
285
|
/** Optional: Custom query parser (default: PrismaQueryParser) */
|
|
@@ -218,7 +292,7 @@ interface PrismaAdapterOptions<TModel> {
|
|
|
218
292
|
declare class PrismaAdapter<TModel = unknown> implements DataAdapter<TModel> {
|
|
219
293
|
readonly type: "prisma";
|
|
220
294
|
readonly name: string;
|
|
221
|
-
readonly repository:
|
|
295
|
+
readonly repository: StandardRepo<TModel>;
|
|
222
296
|
readonly queryParser: PrismaQueryParser;
|
|
223
297
|
private client;
|
|
224
298
|
private modelName;
|
|
@@ -266,4 +340,4 @@ declare class PrismaAdapter<TModel = unknown> implements DataAdapter<TModel> {
|
|
|
266
340
|
*/
|
|
267
341
|
declare function createPrismaAdapter<TModel>(options: PrismaAdapterOptions<TModel>): PrismaAdapter<TModel>;
|
|
268
342
|
//#endregion
|
|
269
|
-
export { PrismaQueryParserOptions as a, MongooseAdapterOptions as c, PrismaQueryParser as i, createMongooseAdapter as l, PrismaAdapterOptions as n, createPrismaAdapter as o, PrismaQueryOptions as r, MongooseAdapter as s, PrismaAdapter as t };
|
|
343
|
+
export { PrismaQueryParserOptions as a, MongooseAdapterOptions as c, DrizzleAdapterOptions as d, createDrizzleAdapter as f, PrismaQueryParser as i, createMongooseAdapter as l, PrismaAdapterOptions as n, createPrismaAdapter as o, PrismaQueryOptions as r, MongooseAdapter as s, PrismaAdapter as t, DrizzleAdapter as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { A as
|
|
6
|
-
import {
|
|
7
|
-
import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-Ck2h67pm.mjs";
|
|
1
|
+
import { a as applyFieldWritePermissions, c as PermissionCheck, d as UserBase, i as applyFieldReadPermissions, l as PermissionContext, n as FieldPermissionMap, o as fields, t as FieldPermission, u as PermissionResult } from "./fields-Lo1VUDpt.mjs";
|
|
2
|
+
import { $ as CrudSchemas, At as SchemaMetadata, B as FastifyWithDecorators, Bt as ArcInternalMetadata, C as ResourceMetadata, Ct as RouteHandler, D as HealthOptions, Dt as FieldMetadata, E as HealthCheck, Et as DataAdapter, Ft as OperationFilter, G as ResourceDefinition, H as RequestWithExtras, It as PipelineConfig, Jt as ServiceContext, K as defineResource, Lt as PipelineContext, Mt as Guard, Nt as Interceptor, O as IntrospectionPluginOptions, Ot as RelationMetadata, Pt as NextFunction, Q as CrudRouteKey, R as FastifyRequestExtras, Rt as PipelineStep, S as RegistryStats, St as IRequestContext, T as GracefulShutdownOptions, Ut as OwnershipCheck, V as MiddlewareHandler, Z as CrudController, _ as ConfigError, _n as UserOrganization, b as IntrospectionData, bt as IController, d as InferAdapterDoc, dn as AnyRecord, et as EventDefinition, f as InferDocType, fn as ApiResponse, g as TypedResourceConfig, gt as RouteSchemaOptions, h as TypedRepository, it as PresetFunction, j as AuthPluginOptions, jt as ValidationResult, k as RequestIdOptions, kt as RepositoryLike, lt as ResourceConfig, m as TypedController, mn as JWTPayload, n as BaseController, nt as MiddlewareConfig, ot as PresetResult, p as InferResourceDoc, pn as ArcRequest, qt as RequestContext, r as BaseControllerOptions, st as RateLimitConfig, t as RouteHandlerMethod, tt as FieldRule, u as PaginationResult, v as ValidateOptions, vn as envelope, vt as ControllerLike, w as CrudRouterOptions, x as RegistryEntry, xt as IControllerResponse, y as ValidationResult$1, z as FastifyWithAuth, zt as Transform } from "./index-Cl0uoKd5.mjs";
|
|
3
|
+
import { l as createMongooseAdapter, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./index-DStwgFUK.mjs";
|
|
4
|
+
import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, m as DEFAULT_MAX_LIMIT, p as DEFAULT_LIMIT, s as defineResourceVariants, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "./index-8qw4y6ff.mjs";
|
|
5
|
+
import { A as requireRoles, C as allOf, E as denyAll, M as when, O as requireAuth, S as createOrgPermissions, T as anyOf, a as presets_d_exports, c as readOnly, d as requireOrgRole, f as requireScopeContext, i as ownerWithAdminBypass, k as requireOwnership, l as requireOrgInScope, m as requireTeamMembership, n as authenticated, o as publicRead, p as requireServiceScope, r as fullPublic, s as publicReadAdminWrite, t as adminOnly, u as requireOrgMembership, v as DynamicPermissionMatrix, w as allowPublic, x as createDynamicPermissionMatrix, y as DynamicPermissionMatrixConfig } from "./index-ChIw3776.mjs";
|
|
6
|
+
import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-CCSsMpXE.mjs";
|
|
8
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
8
|
import { RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
|
|
10
9
|
|
|
@@ -253,4 +252,4 @@ declare function arcLog(module: string): ArcLogger;
|
|
|
253
252
|
//#region src/index.d.ts
|
|
254
253
|
declare const version: string;
|
|
255
254
|
//#endregion
|
|
256
|
-
export { type ValidationResult as AdapterValidationResult, type
|
|
255
|
+
export { type ValidationResult as AdapterValidationResult, type AnyRecord, type ApiResponse, ArcError, type ArcInternalMetadata, type ArcLogWriter, type ArcLogger, type ArcLoggerOptions, type ArcRequest, type AuthPluginOptions, BaseController, type BaseControllerOptions, CRUD_OPERATIONS, type ConfigError, type ControllerLike, type CrudController, CrudOperation, type CrudRouteKey, type CrudRouterOptions, type CrudSchemas, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DataAdapter, type DynamicPermissionMatrix, type DynamicPermissionMatrixConfig, type EventDefinition, type FastifyRequestExtras, type FastifyWithAuth, type FastifyWithDecorators, type FieldMetadata, type FieldPermission, type FieldPermissionMap, type FieldRule, ForbiddenError, type GracefulShutdownOptions, type Guard, HOOK_OPERATIONS, HOOK_PHASES, type HealthCheck, type HealthOptions, HookOperation, HookPhase, type IController, type IControllerResponse, type IRequestContext, type InferAdapterDoc, type InferDocType, type InferResourceDoc, type Interceptor, type IntrospectionData, type IntrospectionPluginOptions, type JWTPayload, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, type MiddlewareConfig, MongooseAdapter, MutationOperation, type NamedMiddleware, NotFoundError, type OwnershipCheck, type PaginationResult, type PermissionCheck, type PermissionContext, type PermissionResult, type PipelineConfig, type PipelineContext, type PipelineStep, type PresetFunction, type PresetResult, PrismaAdapter, RESERVED_QUERY_PARAMS, type RateLimitConfig, type RegistryEntry, type RegistryStats, type RelationMetadata, type RepositoryLike, type RequestContext, type RequestIdOptions, type RequestStore, type RequestWithExtras, type ResourceConfig, ResourceDefinition, type ResourceMetadata, type RouteHandler, type RouteHandlerMethod, type RouteSchemaOptions, SYSTEM_FIELDS, type SchemaMetadata, type ServiceContext, type Transform, type TypedController, type TypedRepository, type TypedResourceConfig, UnauthorizedError, type UserBase, type UserOrganization, type ValidateOptions, ValidationError, type ValidationResult$1 as ValidationResult, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_d_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as
|
|
3
|
-
import { t as BaseController } from "./BaseController-
|
|
4
|
-
import { envelope } from "./types
|
|
5
|
-
import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-
|
|
6
|
-
import { t as
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
import { C as
|
|
10
|
-
import { t as
|
|
11
|
-
import { n as configureArcLogger, t as arcLog } from "./logger-D1YrIImS.mjs";
|
|
1
|
+
import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./adapters-BXY4i-hw.mjs";
|
|
2
|
+
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-BhY1OHoH.mjs";
|
|
3
|
+
import { t as BaseController } from "./BaseController-CbKKIflT.mjs";
|
|
4
|
+
import { t as envelope } from "./types-Csi3FLfq.mjs";
|
|
5
|
+
import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-bxkeltzz.mjs";
|
|
6
|
+
import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-D5c-5BJL.mjs";
|
|
7
|
+
import { t as requestContext } from "./requestContext-xHIKedG6.mjs";
|
|
8
|
+
import { a as formatValidationErrors, h as pipe, i as assertValidConfig, n as ResourceDefinition, o as validateResourceConfig, p as getControllerScope, r as defineResource, t as defineResourceVariants } from "./core-CcR01lup.mjs";
|
|
9
|
+
import { C as requireAuth, D as when, T as requireRoles, _ as requireTeamMembership, a as presets_exports, b as anyOf, c as readOnly, d as createOrgPermissions, f as requireOrgInScope, g as requireServiceScope, h as requireScopeContext, i as ownerWithAdminBypass, m as requireOrgRole, n as authenticated, o as publicRead, p as requireOrgMembership, r as fullPublic, s as publicReadAdminWrite, t as adminOnly, u as createDynamicPermissionMatrix, v as allOf, w as requireOwnership, x as denyAll, y as allowPublic } from "./permissions-Dk6mshja.mjs";
|
|
10
|
+
import { n as configureArcLogger, t as arcLog } from "./logger-DLg8-Ueg.mjs";
|
|
12
11
|
//#region src/middleware/middleware.ts
|
|
13
12
|
/**
|
|
14
13
|
* Named Middleware — Priority-based, conditional middleware execution.
|
|
@@ -128,6 +127,6 @@ function transform(name, handlerOrOptions) {
|
|
|
128
127
|
}
|
|
129
128
|
//#endregion
|
|
130
129
|
//#region src/index.ts
|
|
131
|
-
const version = "2.
|
|
130
|
+
const version = "2.10.3";
|
|
132
131
|
//#endregion
|
|
133
132
|
export { ArcError, BaseController, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MongooseAdapter, NotFoundError, PrismaAdapter, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as DomainEvent } from "../EventTransport-CUw5NNWe.mjs";
|
|
2
2
|
import { WebSocketClient, WebSocketMessage } from "./websocket.mjs";
|
|
3
3
|
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
@@ -4,7 +4,7 @@ const eventGatewayPluginImpl = async (fastify, opts = {}) => {
|
|
|
4
4
|
const { auth = true, orgScoped = false, roomPolicy, maxMessageBytes, maxSubscriptionsPerClient, authenticate } = opts;
|
|
5
5
|
if (auth && !authenticate && !fastify.hasDecorator("authenticate")) throw new Error("[arc-event-gateway] auth is true but fastify.authenticate is not registered. Register an auth plugin first, provide a custom authenticate function, or set auth: false.");
|
|
6
6
|
if (opts.sse !== false) {
|
|
7
|
-
const { default: ssePlugin } = await import("../sse-
|
|
7
|
+
const { default: ssePlugin } = await import("../sse-yBCgOLGu.mjs").then((n) => n.r);
|
|
8
8
|
await fastify.register(ssePlugin, {
|
|
9
9
|
path: opts.sse?.path ?? "/events/stream",
|
|
10
10
|
requireAuth: auth,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { WebSocketClient, WebSocketMessage, WebSocketPluginOptions } from "./websocket.mjs";
|
|
2
2
|
import { EventGatewayOptions } from "./event-gateway.mjs";
|
|
3
3
|
import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
|
|
4
|
-
import { c as McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-
|
|
4
|
+
import { c as McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-Btdda02s.mjs";
|
|
5
5
|
import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
|
|
6
6
|
import { WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription } from "./webhooks.mjs";
|
|
7
7
|
export { type BetterAuthHandler, type CallToolResult, type CreateMcpServerConfig, type CrudOperation, type EventGatewayOptions, type JobDefinition, type JobDispatchOptions, type JobDispatcher, type JobMeta, type JobsPluginOptions, type McpAuthResult, type McpPluginOptions, type McpResourceConfig, type PromptDefinition, type QueueStats, type StreamlinePluginOptions, type ToolAnnotations, type ToolContext, type ToolDefinition, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, type WebhookDeliveryRecord, type WebhookManager, type WebhookPluginOptions, type WebhookStore, type WebhookSubscription, type WorkflowLike, type WorkflowRunLike };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as McpAuthResolver, c as McpResourceConfig, d as SessionEntry, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler, u as PromptResult } from "../../types-
|
|
1
|
+
import { G as ResourceDefinition } from "../../index-Cl0uoKd5.mjs";
|
|
2
|
+
import { a as McpAuthResolver, c as McpResourceConfig, d as SessionEntry, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler, u as PromptResult } from "../../types-Btdda02s.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -233,14 +233,32 @@ declare class McpSessionCache {
|
|
|
233
233
|
}
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region src/integrations/mcp/mcpPlugin.d.ts
|
|
236
|
+
/**
|
|
237
|
+
* Per-prefix MCP registration info. A single fastify instance can register
|
|
238
|
+
* mcpPlugin multiple times under different prefixes (e.g. `/mcp/catalog`,
|
|
239
|
+
* `/mcp/orders`). The decorator is a map keyed by prefix so multi-registration
|
|
240
|
+
* setups can inspect any endpoint's tool list, not just the first one.
|
|
241
|
+
*/
|
|
242
|
+
interface McpRegistration {
|
|
243
|
+
sessions: McpSessionCache | null;
|
|
244
|
+
toolNames: string[];
|
|
245
|
+
resourceNames: string[];
|
|
246
|
+
stateful: boolean;
|
|
247
|
+
}
|
|
248
|
+
interface McpDecorator {
|
|
249
|
+
/** Map of prefix → registration info. Iterate for all endpoints. */
|
|
250
|
+
registrations: Map<string, McpRegistration>;
|
|
251
|
+
/** Shortcut for the first registered prefix (back-compat for single-endpoint apps) */
|
|
252
|
+
readonly sessions: McpSessionCache | null;
|
|
253
|
+
readonly toolNames: string[];
|
|
254
|
+
readonly resourceNames: string[];
|
|
255
|
+
readonly stateful: boolean;
|
|
256
|
+
/** Look up a specific prefix */
|
|
257
|
+
get(prefix: string): McpRegistration | undefined;
|
|
258
|
+
}
|
|
236
259
|
declare module "fastify" {
|
|
237
260
|
interface FastifyInstance {
|
|
238
|
-
mcp?:
|
|
239
|
-
sessions: McpSessionCache | null;
|
|
240
|
-
toolNames: string[];
|
|
241
|
-
resourceNames: string[];
|
|
242
|
-
stateful: boolean;
|
|
243
|
-
};
|
|
261
|
+
mcp?: McpDecorator;
|
|
244
262
|
}
|
|
245
263
|
}
|
|
246
264
|
declare const mcpPlugin: FastifyPluginAsync<McpPluginOptions>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as fieldRulesToZod, r as createMcpServer, t as resourceToTools } from "../../resourceToTools-
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { n as fieldRulesToZod, r as createMcpServer, t as resourceToTools } from "../../resourceToTools-BElv3xPT.mjs";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/integrations/mcp/defineTool.ts
|
|
5
5
|
/**
|
|
@@ -454,13 +454,46 @@ var McpSessionCache = class {
|
|
|
454
454
|
};
|
|
455
455
|
//#endregion
|
|
456
456
|
//#region src/integrations/mcp/mcpPlugin.ts
|
|
457
|
+
/**
|
|
458
|
+
* @classytic/arc — MCP Plugin (Level 1)
|
|
459
|
+
*
|
|
460
|
+
* Fastify plugin that auto-generates MCP tools from Arc resources.
|
|
461
|
+
*
|
|
462
|
+
* Two transport modes:
|
|
463
|
+
* - **Stateless** (default) — fresh server per request, no session tracking.
|
|
464
|
+
* Best for production, horizontal scaling, serverless, edge.
|
|
465
|
+
* - **Stateful** — sessions cached with TTL, reused across requests.
|
|
466
|
+
* Use when you need server-initiated notifications or long-lived connections.
|
|
467
|
+
*
|
|
468
|
+
* Auth is NOT enforced — the plugin respects whatever auth mode you choose:
|
|
469
|
+
* - `auth: false` — no auth, anonymous access (dev/testing/stdio)
|
|
470
|
+
* - `auth: betterAuthInstance` — OAuth 2.1 via Better Auth's mcp() plugin
|
|
471
|
+
* - `auth: async (headers) => {...}` — custom function (API key, JWT, gateway, etc.)
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // Stateless (default) — production, scalable
|
|
476
|
+
* await app.register(mcpPlugin, { resources, auth: false });
|
|
477
|
+
*
|
|
478
|
+
* // Stateful — when you need session persistence
|
|
479
|
+
* await app.register(mcpPlugin, { resources, stateful: true, sessionTtlMs: 600000 });
|
|
480
|
+
*
|
|
481
|
+
* // Multiple MCP endpoints scoped to different resource groups
|
|
482
|
+
* await app.register(mcpPlugin, { resources: catalogResources, prefix: '/mcp/catalog' });
|
|
483
|
+
* await app.register(mcpPlugin, { resources: orderResources, prefix: '/mcp/orders' });
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
457
486
|
const mcpPluginImpl = async (fastify, options) => {
|
|
458
487
|
let StreamableHTTPServerTransport;
|
|
459
488
|
try {
|
|
460
489
|
StreamableHTTPServerTransport = (await import("@modelcontextprotocol/sdk/server/streamableHttp.js")).StreamableHTTPServerTransport;
|
|
490
|
+
} catch {
|
|
491
|
+
throw new Error("@modelcontextprotocol/sdk is required for MCP support. Install it: npm install @modelcontextprotocol/sdk");
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
461
494
|
await import("zod");
|
|
462
495
|
} catch {
|
|
463
|
-
throw new Error("
|
|
496
|
+
throw new Error("zod is required for MCP tool schemas. Install it: npm install zod");
|
|
464
497
|
}
|
|
465
498
|
let enabledResources;
|
|
466
499
|
if (options.include) {
|
|
@@ -516,18 +549,51 @@ const mcpPluginImpl = async (fastify, options) => {
|
|
|
516
549
|
registerStatelessRoutes(fastify, prefix, options, createServerInstance, StreamableHTTPServerTransport, authCache);
|
|
517
550
|
}
|
|
518
551
|
if (cache) fastify.addHook("onClose", async () => cache.close());
|
|
519
|
-
|
|
552
|
+
const registration = {
|
|
520
553
|
sessions: cache,
|
|
521
554
|
toolNames: allTools.map((t) => t.name),
|
|
522
555
|
resourceNames: enabledResources.map((r) => r.name),
|
|
523
556
|
stateful
|
|
524
|
-
}
|
|
557
|
+
};
|
|
558
|
+
if (!fastify.hasDecorator("mcp")) {
|
|
559
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
560
|
+
registrations.set(prefix, registration);
|
|
561
|
+
const first = () => registrations.values().next().value;
|
|
562
|
+
const decorator = {
|
|
563
|
+
registrations,
|
|
564
|
+
get(p) {
|
|
565
|
+
return registrations.get(p);
|
|
566
|
+
},
|
|
567
|
+
get sessions() {
|
|
568
|
+
return first()?.sessions ?? null;
|
|
569
|
+
},
|
|
570
|
+
get toolNames() {
|
|
571
|
+
return first()?.toolNames ?? [];
|
|
572
|
+
},
|
|
573
|
+
get resourceNames() {
|
|
574
|
+
return first()?.resourceNames ?? [];
|
|
575
|
+
},
|
|
576
|
+
get stateful() {
|
|
577
|
+
return first()?.stateful ?? false;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
fastify.decorate("mcp", decorator);
|
|
581
|
+
} else {
|
|
582
|
+
const existing = fastify.mcp;
|
|
583
|
+
if (existing) {
|
|
584
|
+
if (existing.registrations.has(prefix)) throw new Error(`mcpPlugin: prefix "${prefix}" is already registered`);
|
|
585
|
+
existing.registrations.set(prefix, registration);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
525
588
|
};
|
|
526
589
|
function registerStatelessRoutes(fastify, prefix, options, createServer, Transport, authCache) {
|
|
527
590
|
fastify.post(prefix, async (request, reply) => {
|
|
528
591
|
const authResult = await resolveMcpAuth(request.headers, options.auth ?? false, authCache);
|
|
529
592
|
if (!authResult && options.auth) {
|
|
530
|
-
fastify.log.warn(
|
|
593
|
+
fastify.log.warn({
|
|
594
|
+
msg: "mcpPlugin: auth failed",
|
|
595
|
+
status: 401
|
|
596
|
+
});
|
|
531
597
|
return reply.code(401).send({
|
|
532
598
|
jsonrpc: "2.0",
|
|
533
599
|
error: {
|
|
@@ -564,7 +630,10 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
564
630
|
fastify.post(prefix, async (request, reply) => {
|
|
565
631
|
const authResult = await resolveMcpAuth(request.headers, options.auth ?? false);
|
|
566
632
|
if (!authResult && options.auth) {
|
|
567
|
-
fastify.log.warn(
|
|
633
|
+
fastify.log.warn({
|
|
634
|
+
msg: "mcpPlugin: auth failed",
|
|
635
|
+
status: 401
|
|
636
|
+
});
|
|
568
637
|
return reply.code(401).send({
|
|
569
638
|
jsonrpc: "2.0",
|
|
570
639
|
error: {
|
|
@@ -592,15 +661,19 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
592
661
|
}
|
|
593
662
|
}
|
|
594
663
|
const authRef = { current: authResult };
|
|
595
|
-
const transport = new Transport({
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
664
|
+
const transport = new Transport({
|
|
665
|
+
sessionIdGenerator: () => randomUUID(),
|
|
666
|
+
onsessioninitialized: (newSessionId) => {
|
|
667
|
+
cache.set(newSessionId, {
|
|
668
|
+
transport,
|
|
669
|
+
lastAccessed: Date.now(),
|
|
670
|
+
organizationId: authResult?.organizationId ?? "",
|
|
671
|
+
auth: authResult,
|
|
672
|
+
authRef
|
|
673
|
+
});
|
|
674
|
+
}
|
|
603
675
|
});
|
|
676
|
+
await (await createServer(authRef)).connect(transport);
|
|
604
677
|
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
605
678
|
});
|
|
606
679
|
fastify.get(prefix, async (request, reply) => {
|
|
@@ -609,7 +682,10 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
609
682
|
const entry = cache.get(sessionId);
|
|
610
683
|
if (!entry) return reply.code(403).send({ error: "Unauthorized" });
|
|
611
684
|
if (options.auth) {
|
|
612
|
-
|
|
685
|
+
const authResult = await resolveMcpAuth(request.headers, options.auth);
|
|
686
|
+
if (!isSessionOwner(entry, authResult)) return reply.code(403).send({ error: "Unauthorized" });
|
|
687
|
+
entry.auth = authResult;
|
|
688
|
+
entry.authRef.current = authResult;
|
|
613
689
|
}
|
|
614
690
|
cache.touch(sessionId);
|
|
615
691
|
await entry.transport.handleRequest(request.raw, reply.raw);
|
|
@@ -620,7 +696,10 @@ function registerStatefulRoutes(fastify, prefix, options, cache, createServer, T
|
|
|
620
696
|
const entry = cache.get(sessionId);
|
|
621
697
|
if (!entry) return reply.code(204).send();
|
|
622
698
|
if (options.auth) {
|
|
623
|
-
|
|
699
|
+
const authResult = await resolveMcpAuth(request.headers, options.auth);
|
|
700
|
+
if (!isSessionOwner(entry, authResult)) return reply.code(403).send({ error: "Unauthorized" });
|
|
701
|
+
entry.auth = authResult;
|
|
702
|
+
entry.authRef.current = authResult;
|
|
624
703
|
}
|
|
625
704
|
cache.remove(sessionId);
|
|
626
705
|
reply.code(204).send();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as createMcpServer, t as resourceToTools } from "../../resourceToTools-
|
|
1
|
+
import { r as createMcpServer, t as resourceToTools } from "../../resourceToTools-BElv3xPT.mjs";
|
|
2
2
|
//#region src/integrations/mcp/testing.ts
|
|
3
3
|
/**
|
|
4
4
|
* @classytic/arc/mcp/testing — MCP Test Utilities
|
|
@@ -76,6 +76,11 @@ interface VerifySignatureOptions {
|
|
|
76
76
|
* Works with Arc's own `signPayload` format by default (`sha256=<hex>`),
|
|
77
77
|
* but configurable for any HMAC scheme via options.
|
|
78
78
|
*
|
|
79
|
+
* ⚠ The `body` argument must be the exact bytes the sender signed. Fastify
|
|
80
|
+
* parses JSON bodies by default and the re-serialised object will NOT match
|
|
81
|
+
* the original signature. Register `@fastify/raw-body` or use `preParsing`
|
|
82
|
+
* to capture `req.rawBody`, then pass that here.
|
|
83
|
+
*
|
|
79
84
|
* @param body - Raw request body (string or Buffer — must be the exact bytes the sender signed)
|
|
80
85
|
* @param secret - Shared secret between sender and receiver
|
|
81
86
|
* @param signature - The signature header value (e.g. `req.headers['x-webhook-signature']`)
|
|
@@ -46,6 +46,11 @@ function signPayload(payload, secret) {
|
|
|
46
46
|
* Works with Arc's own `signPayload` format by default (`sha256=<hex>`),
|
|
47
47
|
* but configurable for any HMAC scheme via options.
|
|
48
48
|
*
|
|
49
|
+
* ⚠ The `body` argument must be the exact bytes the sender signed. Fastify
|
|
50
|
+
* parses JSON bodies by default and the re-serialised object will NOT match
|
|
51
|
+
* the original signature. Register `@fastify/raw-body` or use `preParsing`
|
|
52
|
+
* to capture `req.rawBody`, then pass that here.
|
|
53
|
+
*
|
|
49
54
|
* @param body - Raw request body (string or Buffer — must be the exact bytes the sender signed)
|
|
50
55
|
* @param secret - Shared secret between sender and receiver
|
|
51
56
|
* @param signature - The signature header value (e.g. `req.headers['x-webhook-signature']`)
|
|
@@ -77,6 +82,7 @@ function signPayload(payload, secret) {
|
|
|
77
82
|
* ```
|
|
78
83
|
*/
|
|
79
84
|
function verifySignature(body, secret, signature, options) {
|
|
85
|
+
if (typeof body !== "string" && !Buffer.isBuffer(body)) throw new TypeError("verifySignature: body must be a string or Buffer (the exact bytes the sender signed). Register @fastify/raw-body and pass req.rawBody — not req.body.");
|
|
80
86
|
if (!signature) return false;
|
|
81
87
|
const prefix = options?.prefix ?? "sha256=";
|
|
82
88
|
const algorithm = options?.algorithm ?? "sha256";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//#region src/cache/interface.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Cache Store Interface — aligned with `@classytic/repo-core/cache.CacheAdapter`.
|
|
4
|
+
*
|
|
5
|
+
* Arc's cache layer speaks the same `get / set(ttlSeconds?) / del / clear(pattern?)`
|
|
6
|
+
* transport-level contract published by `@classytic/repo-core`. One Redis
|
|
7
|
+
* implementation drops into Arc's `QueryCache`, mongokit's cache plugin,
|
|
8
|
+
* sqlitekit's cache plugin, and every future kit without wrapper shims.
|
|
9
|
+
*
|
|
10
|
+
* Arc extends the bare adapter with two optional observability fields —
|
|
11
|
+
* `name` (for diagnostics) and `stats()` (for the response-cache plugin) —
|
|
12
|
+
* that are opt-in: consumers implementing only `CacheAdapter` still
|
|
13
|
+
* structurally satisfy `CacheStore`, so a raw repo-core adapter plugs
|
|
14
|
+
* directly into Arc.
|
|
15
|
+
*
|
|
16
|
+
* ## TTL unit
|
|
17
|
+
*
|
|
18
|
+
* `ttlSeconds`, not milliseconds. Matches Redis (`SET … EX seconds`) which
|
|
19
|
+
* is the dominant backend. `0` or `undefined` means no expiry; implementations
|
|
20
|
+
* may apply their own default.
|
|
21
|
+
*
|
|
22
|
+
* ## Not-found semantics
|
|
23
|
+
*
|
|
24
|
+
* `get()` returns `undefined` on miss / expired. Matches repo-core.
|
|
25
|
+
*
|
|
26
|
+
* ## Sync-or-async
|
|
27
|
+
*
|
|
28
|
+
* Method returns are `Promise<T> | T` — in-memory `Map` adapters can be
|
|
29
|
+
* synchronous; Redis adapters are async. Consumers always `await`, so
|
|
30
|
+
* sync values just short-circuit the microtask.
|
|
31
|
+
*/
|
|
32
|
+
interface CacheLogger {
|
|
33
|
+
warn(message: string, ...args: unknown[]): void;
|
|
34
|
+
error(message: string, ...args: unknown[]): void;
|
|
35
|
+
}
|
|
36
|
+
interface CacheStats {
|
|
37
|
+
/** Number of entries currently stored */
|
|
38
|
+
entries: number;
|
|
39
|
+
/** Estimated memory usage in bytes (-1 if unavailable) */
|
|
40
|
+
memoryBytes: number;
|
|
41
|
+
/** Cache hit count since creation */
|
|
42
|
+
hits: number;
|
|
43
|
+
/** Cache miss count since creation */
|
|
44
|
+
misses: number;
|
|
45
|
+
/** Number of entries evicted since creation */
|
|
46
|
+
evictions: number;
|
|
47
|
+
}
|
|
48
|
+
interface CacheStore<TValue = unknown> {
|
|
49
|
+
/** Store name for logs/diagnostics. Optional to match repo-core's bare `CacheAdapter`. */
|
|
50
|
+
readonly name?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Get a value by key. Returns `undefined` when not found or expired.
|
|
53
|
+
*/
|
|
54
|
+
get(key: string): Promise<TValue | undefined> | TValue | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Store a value with optional TTL (seconds). `0` or `undefined` means
|
|
57
|
+
* no expiry; implementations may apply a default.
|
|
58
|
+
*/
|
|
59
|
+
set(key: string, value: TValue, ttlSeconds?: number): Promise<void> | void;
|
|
60
|
+
/**
|
|
61
|
+
* Delete a single key. No-op when the key doesn't exist.
|
|
62
|
+
*/
|
|
63
|
+
delete(key: string): Promise<void> | void;
|
|
64
|
+
/**
|
|
65
|
+
* Invalidate keys matching a glob pattern (typically `prefix:*`), or
|
|
66
|
+
* every key when `pattern` is omitted.
|
|
67
|
+
*
|
|
68
|
+
* Optional — simpler adapters that can't enumerate keys (some KV stores)
|
|
69
|
+
* may omit this and rely on TTL for eventual consistency. Consumers that
|
|
70
|
+
* need strict invalidation must check for its presence: `store.clear?.(pattern)`.
|
|
71
|
+
*/
|
|
72
|
+
clear?(pattern?: string): Promise<void> | void;
|
|
73
|
+
/** Cache statistics for observability. Optional. */
|
|
74
|
+
stats?(): CacheStats;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { CacheStats as n, CacheStore as r, CacheLogger as t };
|