@classytic/arc 2.22.0 → 2.25.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/dist/{BaseController-C6FlRWxF.mjs → BaseController--M8EMzt0.mjs} +92 -22
- package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
- package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
- package/dist/actionPermissions-CAphcmUG.mjs +24 -0
- package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
- package/dist/audit/index.d.mts +32 -3
- package/dist/audit/index.mjs +7 -4
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +264 -260
- package/dist/{buildHandler-D_2ULWxZ.mjs → buildHandler-Dtwd8fdz.mjs} +1 -1
- package/dist/cache/index.d.mts +41 -3
- package/dist/cache/index.mjs +33 -4
- package/dist/cli/commands/describe.d.mts +1 -1
- 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/cli/index.mjs +2 -2
- package/dist/context/index.mjs +2 -64
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -4
- package/dist/{core-b6fgFgnd.mjs → core-EUNu0u07.mjs} +298 -12
- package/dist/{createActionRouter-h1cEEaEX.mjs → createActionRouter-B6GvAuAX.mjs} +4 -3
- package/dist/{createAggregationRouter-B1C0kvTL.mjs → createAggregationRouter-wzwFa_dW.mjs} +3 -3
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-COpYKt2h.mjs → elevation-CIz1GjGm.mjs} +4 -3
- package/dist/{errorHandler-BTzXSwIg.mjs → errorHandler-Bq2OeEQL.mjs} +10 -7
- package/dist/{eventPlugin-A6EbthXR.d.mts → eventPlugin-Bg3cP2_u.d.mts} +38 -3
- package/dist/{eventPlugin-DJG2D7sy.mjs → eventPlugin-DrQ_FesK.mjs} +22 -4
- package/dist/events/index.d.mts +57 -314
- package/dist/events/index.mjs +168 -93
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +24 -2
- package/dist/factory/index.d.mts +21 -6
- package/dist/factory/index.mjs +58 -18
- package/dist/{fastify-augmentation-9_fuetB4.d.mts → fastify-augmentation-dPNBVeIj.d.mts} +25 -2
- package/dist/{generate-usRSdQmP.mjs → generate-D0wZupBg.mjs} +1 -1
- package/dist/headers-BvlQSDMB.mjs +45 -0
- package/dist/health-CB_m4S32.mjs +377 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +7 -0
- package/dist/idempotency/index.mjs +32 -20
- package/dist/{index-ogIHvOjo.d.mts → index-BMnbzvtO.d.mts} +70 -4
- package/dist/{integrations/jobs.d.mts → index-CG4yqMDB.d.mts} +82 -2
- package/dist/{index-DbtgCOCj.d.mts → index-DPRH0Cme.d.mts} +46 -94
- package/dist/{module-B4u10iOJ.d.mts → index-KUoxjIq1.d.mts} +263 -23
- package/dist/{index-D8MKYGDa.d.mts → index-f4PTrk3B.d.mts} +37 -5
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +6 -5
- package/dist/{init-DleK3p9f.mjs → init-tlRSt667.mjs} +206 -205
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/jobs/index.d.mts +2 -0
- package/dist/integrations/{jobs.mjs → jobs/index.mjs} +110 -67
- package/dist/integrations/mcp/index.d.mts +68 -5
- package/dist/integrations/mcp/index.mjs +2 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +149 -22
- package/dist/integrations/webhooks.mjs +186 -42
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +41 -14
- package/dist/{interface-C1_WLTMK.d.mts → interface-BGZCzOGL.d.mts} +24 -31
- package/dist/interface-n5hpKg1d.mjs +16 -0
- package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
- package/dist/{mcpPlugin-ByA2tf6D.mjs → mcpPlugin-Cb7olw0j.mjs} +316 -45
- package/dist/{memory-CLXA2VeJ.mjs → memory-CmnzXE5Z.mjs} +29 -0
- package/dist/middleware/index.d.mts +17 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/migrations/index.d.mts +94 -44
- package/dist/migrations/index.mjs +196 -44
- package/dist/{multipartBody-CHEQSZ6G.mjs → multipartBody-D0eBvkeq.mjs} +17 -0
- package/dist/{openapi-BRpMZD7C.mjs → openapi-DrLtog_B.mjs} +4 -4
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +4 -3
- package/dist/{permissions-BJMuKMl7.mjs → permissions-BrtiBKGs.mjs} +305 -492
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{actionPermissions-ze70xXJi.mjs → pipeline-BN5G05rd.mjs} +4 -27
- package/dist/plugins/index.d.mts +7 -78
- package/dist/plugins/index.mjs +14 -97
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{index-BEu4wjbL.d.mts → preloadResources-0oe-JSSS.d.mts} +59 -50
- package/dist/{createApp-Ci7yG5gv.mjs → preloadResources-DfyngIN9.mjs} +240 -68
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +3 -3
- package/dist/presets/index.d.mts +2 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-gw0ciIUD.mjs → presets-DwFDVKP0.mjs} +2 -2
- package/dist/{queryCachePlugin-DzqLN904.mjs → queryCachePlugin-DSGuSVLW.mjs} +39 -8
- package/dist/{queryCachePlugin-CfYWq-Ak.d.mts → queryCachePlugin-br4otiqF.d.mts} +2 -2
- package/dist/{registerRoute-xHc42fwB.mjs → rateLimit-DrED-C4v.mjs} +2 -109
- package/dist/realtime-Cuw_deA-.mjs +375 -0
- package/dist/{redis-stream-iVR2l7aS.d.mts → redis-stream-uIaauNpZ.d.mts} +22 -0
- package/dist/registerRoute-v0Wb6Plt.mjs +108 -0
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/renewingLease-3cuQH7h8.mjs +44 -0
- package/dist/{requestContext-BZKOr13H.mjs → requestContext-MrlJwECf.mjs} +2 -2
- package/dist/schedules-CjOd2MG9.mjs +113 -0
- package/dist/{schemaConverter-Ds5reSGs.mjs → schemaConverter-DJvodiHj.mjs} +4 -1
- package/dist/{schemaIR-B-6SPe7g.mjs → schemaIR-Ca7ySNPx.mjs} +1 -1
- package/dist/schemas/index.d.mts +67 -67
- package/dist/schemas/index.mjs +2 -3
- package/dist/scim/index.mjs +1 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +4 -4
- package/dist/scope-DfaPntpo.mjs +36 -0
- package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
- package/dist/{sse-CfUjwjDk.mjs → sse-BQ1w0dfl.mjs} +8 -31
- package/dist/sseStream-BQ7Qaynp.mjs +57 -0
- package/dist/testing/index.d.mts +4 -3
- package/dist/testing/index.mjs +2 -2
- package/dist/transactionContext-PNdoyLLm.mjs +64 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-bHblZ9YN.mjs → types-CSyCxl2V.mjs} +38 -7
- package/dist/{types-DH0GupI7.d.mts → types-DjC-KUCz.d.mts} +118 -20
- package/dist/{types-Csxga7d3.d.mts → types-Dmw-6Qeo.d.mts} +96 -4
- package/dist/{types-Bc42CUyk.d.mts → types-QFGc3Cjv.d.mts} +37 -2
- package/dist/usage/index.d.mts +3 -52
- package/dist/usage/index.mjs +0 -0
- package/dist/userHelpers-Cg_y3fBz.mjs +29 -0
- package/dist/utils/index.d.mts +90 -57
- package/dist/utils/index.mjs +7 -3
- package/dist/{utils-FhJ88TJE.mjs → utils-taUVD4p3.mjs} +47 -385
- package/dist/{validate-C2mGgSXg.mjs → validate-Bk44F2yT.mjs} +1 -1
- package/dist/{versioning-DLwklPlG.d.mts → versioning-DXgY5BUm.d.mts} +52 -31
- package/dist/{versioning-Dk-71iIz.mjs → versioning-gKWyryT3.mjs} +8 -1
- package/dist/{websocket-uuWqM6UY.d.mts → websocket-_Pd0CgR0.d.mts} +15 -1
- package/package.json +526 -453
- package/skills/arc/references/api-reference.md +3 -1
- package/skills/arc/references/integrations.md +20 -11
- package/skills/arc/references/production.md +734 -696
- package/dist/QueryCache-Z1B38koh.d.mts +0 -40
- package/dist/betterAuthOpenApi-CV1lxpRX.mjs +0 -2
- package/dist/health-ScyjIVo5.mjs +0 -202
- package/dist/requestId-5BhTVzBv.mjs +0 -113
- package/dist/scope-k15RXfcq.mjs +0 -64
- package/dist/types-XTApa8b6.mjs +0 -24
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { n as forwardedStreamHeaders } from "./streaming-BQcW6_Kq.mjs";
|
|
2
|
+
//#region src/utils/sseStream.ts
|
|
3
|
+
/**
|
|
4
|
+
* Take over the reply socket and open an SSE stream. Call ONLY after every
|
|
5
|
+
* gate (auth, permissions, validation) has passed — after `hijack()` no
|
|
6
|
+
* JSON error can be sent.
|
|
7
|
+
*/
|
|
8
|
+
function openSseStream(request, reply, options = {}) {
|
|
9
|
+
const { heartbeatMs = 3e4, retryMs } = options;
|
|
10
|
+
reply.hijack();
|
|
11
|
+
reply.raw.writeHead(200, {
|
|
12
|
+
"content-type": "text/event-stream",
|
|
13
|
+
"cache-control": "no-cache",
|
|
14
|
+
connection: "keep-alive",
|
|
15
|
+
"x-accel-buffering": "no",
|
|
16
|
+
...forwardedStreamHeaders(reply)
|
|
17
|
+
});
|
|
18
|
+
reply.raw.flushHeaders();
|
|
19
|
+
if (retryMs !== void 0) reply.raw.write(`retry: ${retryMs}\n\n`);
|
|
20
|
+
const cleanups = [];
|
|
21
|
+
let closed = false;
|
|
22
|
+
const close = () => {
|
|
23
|
+
if (closed) return;
|
|
24
|
+
closed = true;
|
|
25
|
+
clearInterval(heartbeatTimer);
|
|
26
|
+
for (const fn of cleanups) try {
|
|
27
|
+
fn();
|
|
28
|
+
} catch {}
|
|
29
|
+
if (!reply.raw.writableEnded) reply.raw.end();
|
|
30
|
+
};
|
|
31
|
+
const destroyOnBackpressure = (context) => {
|
|
32
|
+
request.raw.destroy(/* @__PURE__ */ new Error(`SSE connection terminated: ${context} backpressure`));
|
|
33
|
+
close();
|
|
34
|
+
};
|
|
35
|
+
const heartbeatTimer = setInterval(() => {
|
|
36
|
+
if (closed) return;
|
|
37
|
+
if (!reply.raw.write(": heartbeat\n\n")) destroyOnBackpressure("heartbeat");
|
|
38
|
+
}, heartbeatMs);
|
|
39
|
+
request.raw.on("close", close);
|
|
40
|
+
return {
|
|
41
|
+
write(eventName, data, id) {
|
|
42
|
+
if (closed) return false;
|
|
43
|
+
const idField = id !== void 0 ? `id: ${id}\n` : "";
|
|
44
|
+
if (!reply.raw.write(`${idField}event: ${eventName}\ndata: ${data}\n\n`)) {
|
|
45
|
+
destroyOnBackpressure("slow client");
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
},
|
|
50
|
+
close,
|
|
51
|
+
onCleanup(fn) {
|
|
52
|
+
cleanups.push(fn);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { openSseStream as t };
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { V as ResourceDefinition
|
|
2
|
-
import {
|
|
3
|
-
import { d as
|
|
1
|
+
import { V as ResourceDefinition } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { c as AnyRecord } from "../types-Dmw-6Qeo.mjs";
|
|
3
|
+
import { d as ArcModuleInput } from "../index-KUoxjIq1.mjs";
|
|
4
|
+
import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-0oe-JSSS.mjs";
|
|
4
5
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
5
6
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
6
7
|
import { Mock } from "vitest";
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-DNre0wRg.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { i as createApp, n as preloadResourcesAsync, p as getModuleExports, t as preloadResources } from "../preloadResources-DfyngIN9.mjs";
|
|
3
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -1116,7 +1116,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1116
1116
|
};
|
|
1117
1117
|
}
|
|
1118
1118
|
async function createTestApp(options = {}) {
|
|
1119
|
-
const { createApp } = await import("../
|
|
1119
|
+
const { createApp } = await import("../preloadResources-DfyngIN9.mjs").then((n) => n.a);
|
|
1120
1120
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1121
1121
|
let dbHandle;
|
|
1122
1122
|
let dbUri;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
//#region src/context/transactionContext.ts
|
|
3
|
+
/**
|
|
4
|
+
* Transaction Context via AsyncLocalStorage
|
|
5
|
+
*
|
|
6
|
+
* Provides a Unit-of-Work (UoW) hook so any code in the call stack can
|
|
7
|
+
* retrieve the active database session/transaction without it being threaded
|
|
8
|
+
* through every function parameter.
|
|
9
|
+
*
|
|
10
|
+
* Arc is DB-agnostic — the session type is `unknown` so any backend
|
|
11
|
+
* (Mongoose session, Prisma transaction, pg client, Drizzle client) can
|
|
12
|
+
* participate. Kit adapters call `runInTransaction(session, fn)` to install
|
|
13
|
+
* the active context; the outbox's `store()` reads it automatically when no
|
|
14
|
+
* explicit `options.session` is passed.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // In a Mongoose-backed service (mongokit adapter)
|
|
19
|
+
* import { transactionContext } from '@classytic/arc/context';
|
|
20
|
+
*
|
|
21
|
+
* await mongoose.connection.withSession(async (session) => {
|
|
22
|
+
* await session.withTransaction(async () => {
|
|
23
|
+
* await transactionContext.run(session, async () => {
|
|
24
|
+
* await orders.insertOne(order, { session });
|
|
25
|
+
* // outbox.store() auto-picks up `session` — no manual threading
|
|
26
|
+
* await outbox.store(orderCreatedEvent);
|
|
27
|
+
* });
|
|
28
|
+
* });
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Opt-out: explicit session always wins over the ALS context
|
|
35
|
+
* await outbox.store(event, { session: explicitSession });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
const storage = new AsyncLocalStorage();
|
|
39
|
+
/**
|
|
40
|
+
* Transaction context API.
|
|
41
|
+
*
|
|
42
|
+
* - `run(session, fn)` — install an active DB session for the duration of `fn`
|
|
43
|
+
* - `get()` — read the current session (undefined if outside a transaction scope)
|
|
44
|
+
*/
|
|
45
|
+
const transactionContext = {
|
|
46
|
+
/**
|
|
47
|
+
* Run `fn` with `session` as the active DB transaction context.
|
|
48
|
+
* Any call to `get()` within `fn`'s async call stack returns `session`.
|
|
49
|
+
*/
|
|
50
|
+
run(session, fn) {
|
|
51
|
+
return storage.run(session, fn);
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Get the current DB session, or `undefined` if not inside a
|
|
55
|
+
* `transactionContext.run(...)` scope.
|
|
56
|
+
*/
|
|
57
|
+
get() {
|
|
58
|
+
return storage.getStore();
|
|
59
|
+
},
|
|
60
|
+
/** The underlying AsyncLocalStorage instance. Exposed for advanced integrations. */
|
|
61
|
+
storage
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { transactionContext as t };
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as OpenApiSchemas,
|
|
2
|
-
import { i as RequestScope } from "../types-
|
|
3
|
-
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-
|
|
1
|
+
import { $ as OpenApiSchemas, A as RequestIdOptions, At as RouteDefinition, Bt as RouteHandler, C as RequestContext, D as HealthCheck, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as AuthenticatorContext, H as CrudController, Ht as AuthHelpers, I as MiddlewareHandler, It as FastifyHandler, J as PresetHook, K as ResolvedTenantPurge, Kt as JwtContext, L as RequestWithExtras, Lt as IController, M as EventsDecorator, Mt as RouteMethod, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, Ot as ActionHandlerFn, P as FastifyWithAuth, Pt as ControllerHandler, Q as MiddlewareConfig, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as AuthPluginOptions, W as ResourcePermissions, Wt as Authenticator, X as CrudRouteKey, Y as PresetResult, Z as CrudSchemas, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kn as BaseControllerOptions, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, qt as TokenPair, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, zt as IRequestContext } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { i as RequestScope } from "../types-DjC-KUCz.mjs";
|
|
3
|
+
import { c as AnyRecord, d as ObjectId, f as UserLike, i as UserBase, l as ArcRequest, n as PermissionContext, p as UserOrganization, r as PermissionResult, t as PermissionCheck, u as JWTPayload } from "../types-Dmw-6Qeo.mjs";
|
|
4
4
|
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-C9s5hFP_.mjs";
|
|
5
|
-
export type { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord,
|
|
5
|
+
export type { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceExtensions, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { l as UnauthorizedError, o as OrgRequiredError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
+
import { n as requireSingleHeaderValue } from "./headers-BvlQSDMB.mjs";
|
|
3
|
+
import { t as getUserId$1 } from "./userHelpers-Cg_y3fBz.mjs";
|
|
2
4
|
//#region src/scope/types.ts
|
|
3
5
|
/**
|
|
4
6
|
* Request Scope — The One Standard
|
|
@@ -87,8 +89,7 @@ function getTenantFromRequest(req, options = {}) {
|
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
const headerName = (options.header ?? "x-organization-id").toLowerCase();
|
|
90
|
-
const
|
|
91
|
-
const headerValue = Array.isArray(raw) ? raw[0] : raw;
|
|
92
|
+
const headerValue = requireSingleHeaderValue(req.headers, headerName);
|
|
92
93
|
if (typeof headerValue === "string" && headerValue.length > 0) return {
|
|
93
94
|
organizationId: headerValue,
|
|
94
95
|
source: "header"
|
|
@@ -259,8 +260,13 @@ function isOrgInScope(scope, targetOrgId) {
|
|
|
259
260
|
* ```
|
|
260
261
|
*/
|
|
261
262
|
function getUserId(scope) {
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
switch (scope.kind) {
|
|
264
|
+
case "authenticated":
|
|
265
|
+
case "member":
|
|
266
|
+
case "elevated": return scope.userId;
|
|
267
|
+
case "public":
|
|
268
|
+
case "service": return;
|
|
269
|
+
}
|
|
264
270
|
}
|
|
265
271
|
/**
|
|
266
272
|
* Throwing variant of `getOrgId`. Returns the organization id when the
|
|
@@ -356,9 +362,12 @@ function getUserRoles(scope) {
|
|
|
356
362
|
*/
|
|
357
363
|
function getOrgContext(request) {
|
|
358
364
|
const scope = request.scope ?? { kind: "public" };
|
|
365
|
+
const requestedOrgId = requireSingleHeaderValue(request.headers, "x-organization-id");
|
|
366
|
+
const rawOrganizationId = request.user?.organizationId;
|
|
367
|
+
const userOrganizationId = rawOrganizationId === void 0 || rawOrganizationId === null ? void 0 : String(rawOrganizationId) || void 0;
|
|
359
368
|
return {
|
|
360
|
-
userId: getUserId(scope) ?? request.user
|
|
361
|
-
organizationId: getOrgId(scope) ??
|
|
369
|
+
userId: getUserId(scope) ?? getUserId$1(request.user),
|
|
370
|
+
organizationId: getOrgId(scope) ?? userOrganizationId ?? requestedOrgId,
|
|
362
371
|
roles: getUserRoles(scope),
|
|
363
372
|
orgRoles: getOrgRoles(scope)
|
|
364
373
|
};
|
|
@@ -389,9 +398,31 @@ function getOrgContext(request) {
|
|
|
389
398
|
function getRequestScope(request) {
|
|
390
399
|
return request.scope ?? PUBLIC_SCOPE;
|
|
391
400
|
}
|
|
401
|
+
function scopeFirstCtx(req, options = {}) {
|
|
402
|
+
let actorId;
|
|
403
|
+
let organizationId;
|
|
404
|
+
const s = req.scope;
|
|
405
|
+
if (s && "kind" in s) {
|
|
406
|
+
actorId = getUserId(s) ?? getClientId(s);
|
|
407
|
+
organizationId = getOrgId(s);
|
|
408
|
+
} else if (s) {
|
|
409
|
+
actorId = s.userId;
|
|
410
|
+
organizationId = s.organizationId;
|
|
411
|
+
}
|
|
412
|
+
actorId = actorId ?? options.fallbackActorId;
|
|
413
|
+
if (!organizationId && options.orgHeader !== false && req.headers) {
|
|
414
|
+
const headerName = (options.orgHeader ?? "x-organization-id").toLowerCase();
|
|
415
|
+
const headerValue = requireSingleHeaderValue(req.headers, headerName);
|
|
416
|
+
if (typeof headerValue === "string" && headerValue.length > 0) organizationId = headerValue;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
...actorId !== void 0 ? { actorId } : {},
|
|
420
|
+
...organizationId !== void 0 ? { organizationId } : {}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
392
423
|
/** Default public scope — used as initial decoration value */
|
|
393
424
|
const PUBLIC_SCOPE = Object.freeze({ kind: "public" });
|
|
394
425
|
/** Default authenticated scope — used when user is logged in but no org */
|
|
395
426
|
const AUTHENTICATED_SCOPE = Object.freeze({ kind: "authenticated" });
|
|
396
427
|
//#endregion
|
|
397
|
-
export { isService as C, requireUserId as D, requireTeamId as E, isOrgInScope as S, requireOrgId as T, getUserRoles as _, getDPoPJkt as a, isElevated as b, getOrgId as c, getScopeContext as d, getScopeContextMap as f, getUserId as g, getTenantFromRequest as h, getClientId as i, getOrgRoles as l, getTeamId as m, PUBLIC_SCOPE as n, getMandate as o, getServiceScopes as p, getAncestorOrgIds as r, getOrgContext as s, AUTHENTICATED_SCOPE as t, getRequestScope as u, hasOrgAccess as v, requireClientId as w, isMember as x, isAuthenticated as y };
|
|
428
|
+
export { isService as C, requireUserId as D, requireTeamId as E, scopeFirstCtx as O, isOrgInScope as S, requireOrgId as T, getUserRoles as _, getDPoPJkt as a, isElevated as b, getOrgId as c, getScopeContext as d, getScopeContextMap as f, getUserId as g, getTenantFromRequest as h, getClientId as i, getOrgRoles as l, getTeamId as m, PUBLIC_SCOPE as n, getMandate as o, getServiceScopes as p, getAncestorOrgIds as r, getOrgContext as s, AUTHENTICATED_SCOPE as t, getRequestScope as u, hasOrgAccess as v, requireClientId as w, isMember as x, isAuthenticated as y };
|
|
@@ -1,24 +1,39 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/utils/headers.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* Canonical header typing + accessor.
|
|
4
|
+
*
|
|
5
|
+
* Node/Fastify header values are `string | string[] | undefined` — repeated
|
|
6
|
+
* headers arrive as arrays. `IRequestContext.headers` is DECLARED as
|
|
7
|
+
* `Record<string, string | undefined>` for ergonomic host code, but the HTTP
|
|
8
|
+
* adapter passes Fastify's headers object through unchanged, so `string[]`
|
|
9
|
+
* CAN appear at runtime behind the narrower type (the declared type is
|
|
10
|
+
* scheduled to widen to {@link ArcHeaders} in v3). Until then, read headers
|
|
11
|
+
* through {@link getHeader} instead of indexing — it's honest about arrays
|
|
12
|
+
* and deterministic about duplicates.
|
|
13
|
+
*/
|
|
14
|
+
/** What a headers bag actually contains at runtime — HTTP or synthetic MCP. */
|
|
15
|
+
type ArcHeaders = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
16
|
+
/**
|
|
17
|
+
* Read a single header value. Lowercases the name (Node normalizes incoming
|
|
18
|
+
* header names to lowercase) and returns the FIRST value when the header
|
|
19
|
+
* was repeated.
|
|
20
|
+
*
|
|
21
|
+
* Duplicate policy: first-value is deterministic, but for security-sensitive
|
|
22
|
+
* headers (authorization material, forwarded-for chains, content-length) a
|
|
23
|
+
* duplicate usually signals smuggling or a broken proxy — consumers that
|
|
24
|
+
* care should check `Array.isArray(headers[name])` and REJECT instead of
|
|
25
|
+
* picking a winner.
|
|
26
|
+
*/
|
|
27
|
+
declare function getHeader(headers: ArcHeaders | Record<string, string | undefined> | undefined, name: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Read an identity-bearing or protocol header and reject repeated values.
|
|
9
30
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* // In a permission check
|
|
13
|
-
* const scope = request.scope;
|
|
14
|
-
* if (isElevated(scope)) return true;
|
|
15
|
-
* if (isMember(scope) && scope.orgRoles.includes('admin')) return true;
|
|
16
|
-
*
|
|
17
|
-
* // Get user identity from scope
|
|
18
|
-
* const userId = getUserId(scope);
|
|
19
|
-
* const globalRoles = getUserRoles(scope);
|
|
20
|
-
* ```
|
|
31
|
+
* Repeated security-sensitive headers are ambiguous at proxy boundaries and
|
|
32
|
+
* may indicate request smuggling. Callers must not silently choose a winner.
|
|
21
33
|
*/
|
|
34
|
+
declare function requireSingleHeaderValue(headers: ArcHeaders | Record<string, string | undefined> | undefined, name: string): string | undefined;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/scope/types.d.ts
|
|
22
37
|
/**
|
|
23
38
|
* A capability mandate — declarative, time-boxed, optionally cap-bounded
|
|
24
39
|
* authorization for a single high-value action by an AI agent or service.
|
|
@@ -118,7 +133,9 @@ interface Mandate {
|
|
|
118
133
|
* | elevated | Platform admin, explicit elevation |
|
|
119
134
|
*
|
|
120
135
|
* **Identity fields by kind:**
|
|
121
|
-
* - `userId`
|
|
136
|
+
* - `userId` — available on `authenticated`, `member`, `elevated` (a real human)
|
|
137
|
+
* - `userRoles` — available on `authenticated` and `member`; elevated scope
|
|
138
|
+
* bypasses role gates explicitly and therefore does not carry role claims
|
|
122
139
|
* - `clientId` — available on `service` (a machine identity, NOT a user)
|
|
123
140
|
* - `organizationId` — required on `member` and `service`, optional on `elevated`
|
|
124
141
|
* - `orgRoles` — org-level roles, only on `member` (from membership records)
|
|
@@ -517,9 +534,90 @@ declare function getOrgContext(request: {
|
|
|
517
534
|
declare function getRequestScope(request: {
|
|
518
535
|
scope?: RequestScope;
|
|
519
536
|
}): RequestScope;
|
|
537
|
+
/** Options for {@link scopeFirstCtx}. */
|
|
538
|
+
interface ScopeFirstCtxOptions {
|
|
539
|
+
/**
|
|
540
|
+
* Tenant-selection header consulted ONLY when the scope carries no org —
|
|
541
|
+
* the custom-auth/public bridge (same semantics as `getTenantFromRequest`).
|
|
542
|
+
* A validated scope org always wins; the header never overrides it.
|
|
543
|
+
* Default `'x-organization-id'`; pass `false` to disable the bridge and
|
|
544
|
+
* read strictly from scope.
|
|
545
|
+
*/
|
|
546
|
+
orgHeader?: string | false;
|
|
547
|
+
/**
|
|
548
|
+
* Actor id substituted when the scope carries no user/client identity
|
|
549
|
+
* (e.g. `'system'` / `'anonymous'`). When provided, the returned `actorId`
|
|
550
|
+
* narrows to `string`; omit to receive `actorId: undefined` on
|
|
551
|
+
* unauthenticated requests and apply your own policy.
|
|
552
|
+
*/
|
|
553
|
+
fallbackActorId?: string;
|
|
554
|
+
}
|
|
555
|
+
/** Return shape of {@link scopeFirstCtx}. */
|
|
556
|
+
interface ScopeFirstCtx {
|
|
557
|
+
actorId?: string;
|
|
558
|
+
organizationId?: string;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Request shape accepted by {@link scopeFirstCtx} — deliberately structural so
|
|
562
|
+
* ONE helper serves both surfaces:
|
|
563
|
+
* - a raw `FastifyRequest`, whose `scope` is the discriminated {@link RequestScope}
|
|
564
|
+
* - arc's `IRequestContext` (controller overrides, pipeline handlers, actions),
|
|
565
|
+
* whose `scope` is the FLAT projection (`{ organizationId?, userId?, orgRoles? }`
|
|
566
|
+
* lifted from `metadata._scope` — see `RequestScopeProjection`)
|
|
567
|
+
*/
|
|
568
|
+
interface ScopeFirstCtxRequest {
|
|
569
|
+
scope?: RequestScope | {
|
|
570
|
+
organizationId?: string | undefined;
|
|
571
|
+
userId?: string | undefined;
|
|
572
|
+
orgRoles?: string[] | undefined;
|
|
573
|
+
} | undefined;
|
|
574
|
+
/** `ArcHeaders` — readonly, repeated headers may arrive as arrays. Fastify's `IncomingHttpHeaders` assigns cleanly. */
|
|
575
|
+
headers?: ArcHeaders | undefined;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* The ONE shared actor/org derivation for resource handlers and module
|
|
579
|
+
* packages (`arc-*` fleet) — scope-FIRST, per the canonical precedence:
|
|
580
|
+
*
|
|
581
|
+
* 1. `request.scope` (arc's validated `RequestScope`, set by auth adapters):
|
|
582
|
+
* `userId` (human) or `clientId` (service) → `actorId`; `getOrgId(scope)`
|
|
583
|
+
* → `organizationId`.
|
|
584
|
+
* 2. The `x-organization-id` header — ONLY when the scope carries no org.
|
|
585
|
+
* This is the bridge for custom-auth/public routes where arc leaves scope
|
|
586
|
+
* unpopulated; it never overrides a validated scope.
|
|
587
|
+
*
|
|
588
|
+
* Replaces per-package hand-rolled clones of
|
|
589
|
+
* `req.user?._id ?? req.user?.id ?? headers['x-organization-id']` — that
|
|
590
|
+
* pattern re-implements (and drifts from) arc's canonical resolution. A
|
|
591
|
+
* package's `ctxOf(req)` should call this for the shared
|
|
592
|
+
* `{ actorId, organizationId }` and only ADD its kernel-specific fields
|
|
593
|
+
* (`actorKind` / `correlationId` / `roles` / `idempotencyKey`).
|
|
594
|
+
*
|
|
595
|
+
* Structural request shape — works on a raw `FastifyRequest` AND on arc's
|
|
596
|
+
* `IRequestContext` (see {@link ScopeFirstCtxRequest}: the discriminated
|
|
597
|
+
* `RequestScope` and the flat controller projection are both understood).
|
|
598
|
+
*
|
|
599
|
+
* @example
|
|
600
|
+
* ```typescript
|
|
601
|
+
* import { scopeFirstCtx } from '@classytic/arc/scope';
|
|
602
|
+
*
|
|
603
|
+
* // Guaranteed actor (kernel context requires one):
|
|
604
|
+
* const { actorId, organizationId } = scopeFirstCtx(req, { fallbackActorId: 'system' });
|
|
605
|
+
*
|
|
606
|
+
* // Optional actor (package applies its own policy):
|
|
607
|
+
* const ctx = scopeFirstCtx(req);
|
|
608
|
+
* if (ctx.actorId) audit.record({ actor: ctx.actorId });
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
declare function scopeFirstCtx(req: ScopeFirstCtxRequest, options: ScopeFirstCtxOptions & {
|
|
612
|
+
fallbackActorId: string;
|
|
613
|
+
}): {
|
|
614
|
+
actorId: string;
|
|
615
|
+
organizationId?: string;
|
|
616
|
+
};
|
|
617
|
+
declare function scopeFirstCtx(req: ScopeFirstCtxRequest, options?: ScopeFirstCtxOptions): ScopeFirstCtx;
|
|
520
618
|
/** Default public scope — used as initial decoration value */
|
|
521
619
|
declare const PUBLIC_SCOPE: Readonly<RequestScope>;
|
|
522
620
|
/** Default authenticated scope — used when user is logged in but no org */
|
|
523
621
|
declare const AUTHENTICATED_SCOPE: Readonly<RequestScope>;
|
|
524
622
|
//#endregion
|
|
525
|
-
export {
|
|
623
|
+
export { requireOrgId as A, hasOrgAccess as C, isOrgInScope as D, isMember as E, getHeader as F, requireSingleHeaderValue as I, requireUserId as M, scopeFirstCtx as N, isService as O, ArcHeaders as P, getUserRoles as S, isElevated as T, getScopeContextMap as _, ScopeFirstCtx as a, getTenantFromRequest as b, getAncestorOrgIds as c, getMandate as d, getOrgContext as f, getScopeContext as g, getRequestScope as h, RequestScope as i, requireTeamId as j, requireClientId as k, getClientId as l, getOrgRoles as m, Mandate as n, ScopeFirstCtxOptions as o, getOrgId as p, PUBLIC_SCOPE as r, ScopeFirstCtxRequest as s, AUTHENTICATED_SCOPE as t, getDPoPJkt as u, getServiceScopes as v, isAuthenticated as w, getUserId as x, getTeamId as y };
|
|
@@ -1,5 +1,99 @@
|
|
|
1
|
-
import { i as RequestScope } from "./types-
|
|
1
|
+
import { i as RequestScope } from "./types-DjC-KUCz.mjs";
|
|
2
2
|
import { FastifyRequest } from "fastify";
|
|
3
|
+
//#region src/types/base.d.ts
|
|
4
|
+
declare module "fastify" {
|
|
5
|
+
interface FastifyRequest {
|
|
6
|
+
/** Request scope — set by auth adapter, read by permissions/presets/guards */
|
|
7
|
+
scope: RequestScope;
|
|
8
|
+
/**
|
|
9
|
+
* Current user — set by auth adapter (Better Auth, JWT, custom).
|
|
10
|
+
* `undefined` on public routes (`auth: false`) or unauthenticated requests.
|
|
11
|
+
* Guard with `if (request.user)` on routes that allow anonymous access.
|
|
12
|
+
*
|
|
13
|
+
* Kept as required (not `user?`) because `@fastify/jwt` declares it
|
|
14
|
+
* as required — declaration merges must have identical modifiers.
|
|
15
|
+
* The `| undefined` in the type achieves the same DX.
|
|
16
|
+
*/
|
|
17
|
+
user: Record<string, unknown> | undefined;
|
|
18
|
+
/** Policy-injected query filters (e.g. ownership, org-scoping) */
|
|
19
|
+
_policyFilters?: Record<string, unknown>;
|
|
20
|
+
/** Field mask — fields to include/exclude in responses */
|
|
21
|
+
fieldMask?: {
|
|
22
|
+
include?: string[];
|
|
23
|
+
exclude?: string[];
|
|
24
|
+
};
|
|
25
|
+
/** Arbitrary policy metadata for downstream consumers */
|
|
26
|
+
policyMetadata?: Record<string, unknown>;
|
|
27
|
+
/** Document loaded by policy middleware for ownership checks */
|
|
28
|
+
document?: unknown;
|
|
29
|
+
/** Ownership check context (field name + user field) */
|
|
30
|
+
_ownershipCheck?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
type AnyRecord = Record<string, unknown>;
|
|
34
|
+
/** MongoDB ObjectId — accepts string or any object with a `toString()` (e.g. mongoose ObjectId). */
|
|
35
|
+
type ObjectId = string | {
|
|
36
|
+
toString(): string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Flexible user type that accepts any object with id/_id properties.
|
|
40
|
+
* The actual user structure is defined by your app's auth system.
|
|
41
|
+
*/
|
|
42
|
+
type UserLike = UserBase & {
|
|
43
|
+
/** User email (optional) */
|
|
44
|
+
email?: string;
|
|
45
|
+
};
|
|
46
|
+
interface UserOrganization {
|
|
47
|
+
userId: string;
|
|
48
|
+
organizationId: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
}
|
|
51
|
+
interface JWTPayload {
|
|
52
|
+
sub: string;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Typed Fastify request with Arc decorations. Use in `raw: true` handlers
|
|
57
|
+
* instead of `(req as any).user`.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* import type { ArcRequest } from '@classytic/arc';
|
|
62
|
+
*
|
|
63
|
+
* handler: async (req: ArcRequest, reply) => {
|
|
64
|
+
* req.user?.id; // typed
|
|
65
|
+
* req.scope.organizationId; // typed (when member)
|
|
66
|
+
* req.signal; // AbortSignal (Fastify 5)
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
type ArcRequest = FastifyRequest & {
|
|
71
|
+
scope: RequestScope;
|
|
72
|
+
user: Record<string, unknown> | undefined;
|
|
73
|
+
signal: AbortSignal;
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/utils/userHelpers.d.ts
|
|
77
|
+
/**
|
|
78
|
+
* Extract a user ID from a user object. Accepts `id` or `_id` — returns
|
|
79
|
+
* `undefined` when neither is present. Used by arc's controllers to
|
|
80
|
+
* populate `createdBy` / `updatedBy` fields and for cache scoping.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { getUserId } from '@classytic/arc/utils';
|
|
85
|
+
* const uid = getUserId(request.user);
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
declare function getUserId(user: UserLike | null | undefined): string | undefined;
|
|
89
|
+
/** Normalize a raw role value from Better Auth, JWT, or a custom identity provider. */
|
|
90
|
+
declare function normalizeRoles(value: unknown): string[];
|
|
91
|
+
/** Extract normalized roles from a user-like object. */
|
|
92
|
+
declare function getUserRoles(user: {
|
|
93
|
+
role?: unknown;
|
|
94
|
+
roles?: unknown;
|
|
95
|
+
} | null | undefined): string[];
|
|
96
|
+
//#endregion
|
|
3
97
|
//#region src/permissions/types.d.ts
|
|
4
98
|
/**
|
|
5
99
|
* User base interface - minimal shape Arc expects
|
|
@@ -24,8 +118,6 @@ interface UserBase {
|
|
|
24
118
|
* Normalize a raw role value (string, comma-separated string, or array) into a string[].
|
|
25
119
|
* Shared low-level helper used by both getUserRoles() and the Better Auth adapter.
|
|
26
120
|
*/
|
|
27
|
-
declare function normalizeRoles(value: unknown): string[];
|
|
28
|
-
declare function getUserRoles(user: UserBase | null | undefined): string[];
|
|
29
121
|
/**
|
|
30
122
|
* Context passed to permission check functions
|
|
31
123
|
*/
|
|
@@ -195,4 +287,4 @@ interface PermissionCheckMeta {
|
|
|
195
287
|
};
|
|
196
288
|
}
|
|
197
289
|
//#endregion
|
|
198
|
-
export {
|
|
290
|
+
export { getUserId as a, AnyRecord as c, ObjectId as d, UserLike as f, UserBase as i, ArcRequest as l, PermissionContext as n, getUserRoles as o, UserOrganization as p, PermissionResult as r, normalizeRoles as s, PermissionCheck as t, JWTPayload as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as ResourceDefinition } from "./index-
|
|
1
|
+
import { V as ResourceDefinition } from "./index-DPRH0Cme.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/integrations/mcp/types.d.ts
|
|
4
4
|
/** Behavioral hints for MCP clients (tool annotations per MCP spec) */
|
|
@@ -157,6 +157,41 @@ interface CrudDescriptionMeta {
|
|
|
157
157
|
* Arc's auto-derived filterable-fields blurb.
|
|
158
158
|
*/
|
|
159
159
|
type CrudDescriptionOverride = string | ((meta: CrudDescriptionMeta) => string);
|
|
160
|
+
/**
|
|
161
|
+
* Execution wiring for MCP tools — the app-level services HTTP requests
|
|
162
|
+
* receive via Fastify decorators (`fastify.arc.hooks`, `fastify.events`,
|
|
163
|
+
* `fastify.audit`, `fastify.log`, `fastify.idempotency`) and MCP's
|
|
164
|
+
* synthetic-context path historically dropped.
|
|
165
|
+
*
|
|
166
|
+
* When present, tool handlers thread it into the request context so the
|
|
167
|
+
* controller pipeline achieves FULL HTTP parity: resource hooks run
|
|
168
|
+
* (before/around/after), the arcCorePlugin after-hook publishes
|
|
169
|
+
* `<resource>.<op>d` events (→ realtime feeds, webhooks, event-driven
|
|
170
|
+
* cache invalidation all see MCP-originated mutations), `ctx.server.*`
|
|
171
|
+
* works inside handlers, and mutating CRUD tools accept an optional
|
|
172
|
+
* `_idempotencyKey` input for retry-safe execution.
|
|
173
|
+
*
|
|
174
|
+
* `mcpPlugin` builds this automatically from its Fastify instance (lazy
|
|
175
|
+
* getters — decorator registration order doesn't matter). Standalone
|
|
176
|
+
* `resourceToTools()` / `createMcpServer()` callers pass their own, or
|
|
177
|
+
* omit it to keep the legacy dispatch-only behavior.
|
|
178
|
+
*/
|
|
179
|
+
interface McpExecutionWiring {
|
|
180
|
+
/** Shared `HookSystem` — `fastify.arc.hooks`. */
|
|
181
|
+
readonly hooks?: unknown;
|
|
182
|
+
/** Event bus decorator — `fastify.events`. */
|
|
183
|
+
readonly events?: unknown;
|
|
184
|
+
/** Audit decorator — `fastify.audit`. */
|
|
185
|
+
readonly audit?: unknown;
|
|
186
|
+
/** Structured logger — `fastify.log`. */
|
|
187
|
+
readonly log?: unknown;
|
|
188
|
+
/**
|
|
189
|
+
* Idempotency store (the `IdempotencyStore` contract from
|
|
190
|
+
* `@classytic/arc/idempotency`). Presence advertises `_idempotencyKey`
|
|
191
|
+
* on mutating CRUD tools and enables executor-level check/lock/replay.
|
|
192
|
+
*/
|
|
193
|
+
readonly idempotencyStore?: unknown;
|
|
194
|
+
}
|
|
160
195
|
/** Per-resource MCP configuration overrides */
|
|
161
196
|
interface McpResourceConfig {
|
|
162
197
|
/** Which CRUD operations to expose (default: all enabled on the resource) */
|
|
@@ -392,4 +427,4 @@ interface CreateMcpServerConfig {
|
|
|
392
427
|
/** CRUD operation type */
|
|
393
428
|
type CrudOperation = "list" | "get" | "create" | "update" | "delete";
|
|
394
429
|
//#endregion
|
|
395
|
-
export { CrudDescriptionOverride as a, McpAuthResult as c,
|
|
430
|
+
export { ToolDefinition as _, CrudDescriptionOverride as a, McpAuthResult as c, McpResourceConfig as d, PromptDefinition as f, ToolContext as g, ToolAnnotations as h, CrudDescriptionMeta as i, McpExecutionWiring as l, SessionEntry as m, CallToolResult as n, CrudOperation as o, PromptResult as p, CreateMcpServerConfig as r, McpAuthResolver as s, BetterAuthHandler as t, McpPluginOptions as u };
|
package/dist/usage/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { i as RequestScope } from "../types-
|
|
2
|
-
import { n as PermissionContext, t as PermissionCheck } from "../types-
|
|
1
|
+
import { i as RequestScope } from "../types-DjC-KUCz.mjs";
|
|
2
|
+
import { n as PermissionContext, t as PermissionCheck } from "../types-Dmw-6Qeo.mjs";
|
|
3
|
+
import { UsageBucket, UsageStore, usagePeriod } from "@classytic/repo-core/usage";
|
|
3
4
|
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
4
5
|
//#region src/usage/requireQuota.d.ts
|
|
5
6
|
interface QuotaOptions {
|
|
@@ -28,56 +29,6 @@ interface QuotaOptions {
|
|
|
28
29
|
}
|
|
29
30
|
declare function requireQuota(options: QuotaOptions): PermissionCheck;
|
|
30
31
|
//#endregion
|
|
31
|
-
//#region src/usage/stores/interface.d.ts
|
|
32
|
-
/**
|
|
33
|
-
* Usage store contract — period-bucketed counters per actor.
|
|
34
|
-
*
|
|
35
|
-
* CANONICAL HOME: `@classytic/repo-core/usage` (>=0.10) owns this
|
|
36
|
-
* contract for the ecosystem — kits implement it there without ever
|
|
37
|
-
* depending on arc (`@classytic/mongokit/usage`, ...). This file is a
|
|
38
|
-
* deliberate STRUCTURAL MIRROR of that shape — same trick as
|
|
39
|
-
* `ScheduleLockLike` vs repo-core's `LockAdapter` — so arc's emitted
|
|
40
|
-
* declarations carry no repo-core@>=0.10 subpath reference and the
|
|
41
|
-
* peer floor stays honest. Any conforming adapter assigns directly.
|
|
42
|
-
*
|
|
43
|
-
* The deliberately tiny surface (one increment, one read) is what makes
|
|
44
|
-
* every backend trivial: memory (dev/tests), Redis (`HINCRBY`), or any kit
|
|
45
|
-
* (Mongo `$inc` upsert) implements it in a handful of lines. Aggregation
|
|
46
|
-
* happens IN the store — arc never buffers counters in process memory
|
|
47
|
-
* beyond the built-in memory store, so multi-replica deployments stay
|
|
48
|
-
* correct as long as the store is shared.
|
|
49
|
-
*
|
|
50
|
-
* This is the HORIZONTAL usage layer (per-actor, per-period platform
|
|
51
|
-
* accounting for quotas/plans/billing). Itemized vertical records — e.g.
|
|
52
|
-
* `@classytic/arc-ai/usage`'s per-run AI token/cost tracking — keep their
|
|
53
|
-
* own richer stores and can sink into this one (`kind: 'ai.tokens'`).
|
|
54
|
-
*/
|
|
55
|
-
/** One counter cell: (actor, period, kind). */
|
|
56
|
-
interface UsageBucket {
|
|
57
|
-
/** Who consumed — org / user / client id, or an IP-derived fallback. */
|
|
58
|
-
actor: string;
|
|
59
|
-
/** Aggregation period, `YYYY-MM` (see `usagePeriod()`). */
|
|
60
|
-
period: string;
|
|
61
|
-
/** Namespaced counter, dot-separated: `api.requests`, `ai.tokens`, `storage.bytes`. */
|
|
62
|
-
kind: string;
|
|
63
|
-
}
|
|
64
|
-
interface UsageStore {
|
|
65
|
-
/** Store name for diagnostics (e.g. 'memory', 'redis', 'mongo'). */
|
|
66
|
-
readonly name: string;
|
|
67
|
-
/** Add `amount` to the bucket's counter, creating it at 0 first. MUST be atomic per bucket. */
|
|
68
|
-
increment(bucket: UsageBucket, amount: number): Promise<void> | void;
|
|
69
|
-
/** Every counter for an actor in a period: `{ 'api.requests': 40231, ... }`. */
|
|
70
|
-
summary(actor: string, period: string): Promise<Record<string, number>>;
|
|
71
|
-
/** Optional cleanup hook (connections, timers). */
|
|
72
|
-
close?(): Promise<void>;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Canonical period key for a date — calendar month, UTC: `2026-07`.
|
|
76
|
-
* Monthly is the billing-native granularity; stores that want finer
|
|
77
|
-
* windows can shard internally without changing the contract.
|
|
78
|
-
*/
|
|
79
|
-
declare function usagePeriod(date?: Date): string;
|
|
80
|
-
//#endregion
|
|
81
32
|
//#region src/usage/stores/memory.d.ts
|
|
82
33
|
declare class MemoryUsageStore implements UsageStore {
|
|
83
34
|
#private;
|
package/dist/usage/index.mjs
CHANGED
|
Binary file
|