@classytic/arc 2.16.1 → 2.17.1
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 +22 -2
- package/dist/{BaseController-Vk8I7C0x.mjs → BaseController-CgHomZ4d.mjs} +5 -47
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/redis-session.mjs +28 -2
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-wKeGXn38.mjs → core-D8tpQ4ZL.mjs} +174 -31
- package/dist/{createActionRouter-eiOFtnC3.mjs → createActionRouter-BHUMb1-y.mjs} +5 -2
- package/dist/{createAggregationRouter-C4unhBnV.mjs → createAggregationRouter-BYVvhfc5.mjs} +1 -1
- package/dist/{createApp-DdPBtmzp.mjs → createApp-CzCEEsEm.mjs} +2 -2
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-Dci0AYLT.mjs → elevation-6mfTLBC-.mjs} +1 -1
- package/dist/{errorHandler-DNPzqaTT.mjs → errorHandler-wQejMBT3.mjs} +45 -4
- package/dist/events/transports/redis-stream-entry.mjs +34 -1
- package/dist/events/transports/redis.mjs +17 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +1 -1
- package/dist/{fastify-augmentation-CJm0vh-l.d.mts → fastify-augmentation-7LIR0nDv.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +20 -4
- package/dist/{index-DBDgy6Hw.d.mts → index-C1JSBan-.d.mts} +177 -1
- package/dist/{index-CC_pgZjY.d.mts → index-CruZir1o.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +5 -5
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/jobs.d.mts +36 -1
- package/dist/integrations/jobs.mjs +82 -9
- package/dist/integrations/mcp/index.d.mts +65 -3
- package/dist/integrations/mcp/index.mjs +93 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/websocket.mjs +2 -1
- package/dist/{mcpPlugin-Dbgz9_Rj.mjs → mcpPlugin-C6FViZXw.mjs} +111 -8
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{multipartBody-BOvVSVCD.mjs → multipartBody-VjVWmZl8.mjs} +2 -1
- package/dist/{openapi-BRnd3pT7.mjs → openapi-DKDs9NfD.mjs} +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-CTxMrreC.mjs → permissions-B1Qy55fg.mjs} +169 -169
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +3 -3
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +1 -1
- package/dist/presets/filesUpload.mjs +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +1 -1
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-C9BE6WaZ.mjs → presets-BYbPobbF.mjs} +5 -3
- package/dist/registry/index.d.mts +1 -1
- package/dist/{routerShared-ClV4cCaa.mjs → routerShared-nCyJGOKX.mjs} +127 -4
- package/dist/scope/index.mjs +2 -2
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +1 -1
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-Dw--SyTq.d.mts → types-BRmS8TNa.d.mts} +1 -1
- package/dist/{types-BD0BCdf6.d.mts → types-CoMjNmkF.d.mts} +12 -1
- package/dist/utils/index.d.mts +79 -2
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-DPXZnZUx.mjs → utils-BMQK8mRl.mjs} +111 -2
- package/package.json +1 -1
- package/skills/arc/SKILL.md +43 -1
- package/skills/arc/references/api-reference.md +2 -0
- package/skills/arc/references/mcp.md +30 -1
- /package/dist/{types-D57iXYb8.mjs → types-DV9WDfeg.mjs} +0 -0
|
@@ -16,6 +16,30 @@ import fp from "fastify-plugin";
|
|
|
16
16
|
function defineJob(definition) {
|
|
17
17
|
return definition;
|
|
18
18
|
}
|
|
19
|
+
var Semaphore = class {
|
|
20
|
+
available;
|
|
21
|
+
waiters = [];
|
|
22
|
+
constructor(permits) {
|
|
23
|
+
this.available = permits;
|
|
24
|
+
}
|
|
25
|
+
get isFull() {
|
|
26
|
+
return this.available === 0;
|
|
27
|
+
}
|
|
28
|
+
acquire() {
|
|
29
|
+
if (this.available > 0) {
|
|
30
|
+
this.available--;
|
|
31
|
+
return Promise.resolve();
|
|
32
|
+
}
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
this.waiters.push(resolve);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
release() {
|
|
38
|
+
const next = this.waiters.shift();
|
|
39
|
+
if (next) next();
|
|
40
|
+
else this.available++;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
19
43
|
const jobsPluginImpl = async (fastify, options) => {
|
|
20
44
|
const { connection, jobs, prefix = "/jobs", bridgeEvents = true, defaults = {} } = options;
|
|
21
45
|
let Queue;
|
|
@@ -33,6 +57,7 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
33
57
|
const queues = /* @__PURE__ */ new Map();
|
|
34
58
|
const dlqQueues = /* @__PURE__ */ new Map();
|
|
35
59
|
const workers = /* @__PURE__ */ new Map();
|
|
60
|
+
const throttledJobs = /* @__PURE__ */ new Map();
|
|
36
61
|
for (const job of jobs) {
|
|
37
62
|
if (!job.repeat) continue;
|
|
38
63
|
const { pattern, every, tz } = job.repeat;
|
|
@@ -66,24 +91,39 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
66
91
|
dlqQueues.set(dlqName, dlqQueue);
|
|
67
92
|
}
|
|
68
93
|
const jobTimeout = job.timeout ?? defaults.timeout;
|
|
94
|
+
const semaphore = job.maxConcurrent != null ? new Semaphore(job.maxConcurrent) : null;
|
|
95
|
+
const throttledSet = /* @__PURE__ */ new Set();
|
|
96
|
+
throttledJobs.set(queueName, throttledSet);
|
|
69
97
|
const worker = new Worker(queueName, async (bullJob) => {
|
|
70
98
|
const meta = {
|
|
71
99
|
jobId: bullJob.id ?? "",
|
|
72
100
|
attemptsMade: bullJob.attemptsMade ?? 0,
|
|
73
101
|
timestamp: Date.now()
|
|
74
102
|
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let timer;
|
|
78
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
79
|
-
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Job '${queueName}' timed out after ${jobTimeout}ms`)), jobTimeout);
|
|
80
|
-
});
|
|
103
|
+
if (semaphore) {
|
|
104
|
+
if (semaphore.isFull) throttledSet.add(bullJob.id ?? "");
|
|
81
105
|
try {
|
|
82
|
-
|
|
106
|
+
await semaphore.acquire();
|
|
83
107
|
} finally {
|
|
84
|
-
|
|
108
|
+
throttledSet.delete(bullJob.id ?? "");
|
|
85
109
|
}
|
|
86
|
-
}
|
|
110
|
+
}
|
|
111
|
+
let result;
|
|
112
|
+
try {
|
|
113
|
+
if (jobTimeout) {
|
|
114
|
+
let timer;
|
|
115
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
116
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Job '${queueName}' timed out after ${jobTimeout}ms`)), jobTimeout);
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
result = await Promise.race([job.handler(bullJob.data, meta), timeoutPromise]);
|
|
120
|
+
} finally {
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
}
|
|
123
|
+
} else result = await job.handler(bullJob.data, meta);
|
|
124
|
+
} finally {
|
|
125
|
+
semaphore?.release();
|
|
126
|
+
}
|
|
87
127
|
if (bridgeEvents && fastify.events?.publish) try {
|
|
88
128
|
await fastify.events.publish(`job.${queueName}.completed`, {
|
|
89
129
|
jobId: bullJob.id,
|
|
@@ -197,6 +237,27 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
197
237
|
}
|
|
198
238
|
return stats;
|
|
199
239
|
},
|
|
240
|
+
async getStatus(jobId) {
|
|
241
|
+
for (const [name, queue] of queues) {
|
|
242
|
+
const job = await queue.getJob(jobId);
|
|
243
|
+
if (!job) continue;
|
|
244
|
+
const state = typeof job.getState === "function" ? await job.getState() : "unknown";
|
|
245
|
+
const throttled = throttledJobs.get(name)?.has(jobId) || void 0;
|
|
246
|
+
return {
|
|
247
|
+
id: jobId,
|
|
248
|
+
name,
|
|
249
|
+
state,
|
|
250
|
+
progress: job.progress ?? 0,
|
|
251
|
+
throttled: throttled === true ? true : void 0,
|
|
252
|
+
timestamp: job.timestamp ?? void 0,
|
|
253
|
+
processedOn: job.processedOn ?? void 0,
|
|
254
|
+
finishedOn: job.finishedOn ?? void 0,
|
|
255
|
+
failedReason: job.failedReason ?? void 0,
|
|
256
|
+
returnValue: job.returnvalue ?? void 0
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
},
|
|
200
261
|
async close() {
|
|
201
262
|
await Promise.all(Array.from(workers.values()).map((w) => w.pause().catch(() => {})));
|
|
202
263
|
const closePromises = [];
|
|
@@ -213,6 +274,18 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
213
274
|
data: await dispatcher.getStats()
|
|
214
275
|
};
|
|
215
276
|
});
|
|
277
|
+
fastify.get(`${prefix}/:id/status`, async (req, reply) => {
|
|
278
|
+
const { id } = req.params;
|
|
279
|
+
const status = await dispatcher.getStatus(id);
|
|
280
|
+
if (!status) return reply.status(404).send({
|
|
281
|
+
success: false,
|
|
282
|
+
error: "Job not found"
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
success: true,
|
|
286
|
+
data: status
|
|
287
|
+
};
|
|
288
|
+
});
|
|
216
289
|
fastify.addHook("onClose", async () => {
|
|
217
290
|
await dispatcher.close();
|
|
218
291
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { V as ResourceDefinition, bt as IRequestContext, yt as IControllerResponse } from "../../index-
|
|
1
|
+
import { V as ResourceDefinition, bt as IRequestContext, yt as IControllerResponse } from "../../index-C1JSBan-.mjs";
|
|
2
2
|
import { i as RequestScope } from "../../types-CfapW2t8.mjs";
|
|
3
3
|
import { c as PermissionCheck } from "../../fields-DqZLp5d6.mjs";
|
|
4
|
-
import { a as CrudDescriptionOverride, c as McpAuthResult, d as PromptDefinition, f as PromptResult, g as ToolDefinition, h as ToolContext, i as CrudDescriptionMeta, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, p as SessionEntry, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpResourceConfig } from "../../types-
|
|
4
|
+
import { a as CrudDescriptionOverride, c as McpAuthResult, d as PromptDefinition, f as PromptResult, g as ToolDefinition, h as ToolContext, i as CrudDescriptionMeta, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, p as SessionEntry, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpResourceConfig } from "../../types-CoMjNmkF.mjs";
|
|
5
5
|
import { ErrorContract } from "@classytic/repo-core/errors";
|
|
6
6
|
import { FastifyPluginAsync } from "fastify";
|
|
7
7
|
import { z } from "zod";
|
|
@@ -54,6 +54,47 @@ interface BuildMcpToolsFromBridgesOptions {
|
|
|
54
54
|
*/
|
|
55
55
|
declare function buildMcpToolsFromBridges(bridges: readonly McpBridge[], options?: BuildMcpToolsFromBridgesOptions): ToolDefinition[];
|
|
56
56
|
//#endregion
|
|
57
|
+
//#region src/integrations/mcp/betterAuthApiKey.d.ts
|
|
58
|
+
/** Minimal subset of Better Auth's API-key record the helper needs. */
|
|
59
|
+
interface BetterAuthKeyRecord {
|
|
60
|
+
id?: string;
|
|
61
|
+
userId?: string;
|
|
62
|
+
referenceId?: string;
|
|
63
|
+
metadata?: unknown;
|
|
64
|
+
permissions?: unknown;
|
|
65
|
+
scopes?: unknown;
|
|
66
|
+
expiresAt?: string | Date | null;
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
name?: string | null;
|
|
69
|
+
}
|
|
70
|
+
interface CreateMcpAuthFromBetterAuthApiKeyOptions {
|
|
71
|
+
/**
|
|
72
|
+
* How to resolve the organization id from a verified key.
|
|
73
|
+
*
|
|
74
|
+
* - `true` (default) — read `key.metadata.organizationId`.
|
|
75
|
+
* - `false` — never set `organizationId` (single-tenant deployments).
|
|
76
|
+
* - `(metadata, key) => string | undefined` — custom extractor, e.g.
|
|
77
|
+
* `(m) => m?.tenantId` for hosts that named the column differently.
|
|
78
|
+
*/
|
|
79
|
+
orgFromMetadata?: boolean | ((metadata: unknown, key: BetterAuthKeyRecord) => string | undefined);
|
|
80
|
+
/**
|
|
81
|
+
* Field name on `metadata` that holds the org id. Default
|
|
82
|
+
* `"organizationId"`. Ignored when `orgFromMetadata` is a function.
|
|
83
|
+
*/
|
|
84
|
+
metadataOrgField?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Permission scope name inside `key.permissions` that should be flattened
|
|
87
|
+
* onto `McpAuthResult.scopes`. Default `"mcp"` — matches Better Auth's
|
|
88
|
+
* docs example. Set to `false` to keep scopes empty.
|
|
89
|
+
*/
|
|
90
|
+
permissionScope?: string | false;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build an {@link McpAuthResolver} that authenticates MCP requests against
|
|
94
|
+
* Better Auth's API-key plugin. See module docstring for the rationale.
|
|
95
|
+
*/
|
|
96
|
+
declare function createMcpAuthFromBetterAuthApiKey(auth: BetterAuthHandler, options?: CreateMcpAuthFromBetterAuthApiKeyOptions): McpAuthResolver;
|
|
97
|
+
//#endregion
|
|
57
98
|
//#region src/integrations/mcp/createMcpServer.d.ts
|
|
58
99
|
/**
|
|
59
100
|
* Mutable auth ref — updated per-request by mcpPlugin.
|
|
@@ -69,6 +110,27 @@ interface AuthRef {
|
|
|
69
110
|
* @returns McpServer instance (not yet connected to a transport)
|
|
70
111
|
*/
|
|
71
112
|
declare function createMcpServer(config: CreateMcpServerConfig, authRef?: AuthRef): Promise<McpServerInstance>;
|
|
113
|
+
/**
|
|
114
|
+
* Detect and resolve duplicate MCP tool names across the assembled tool list.
|
|
115
|
+
*
|
|
116
|
+
* Two outcomes:
|
|
117
|
+
*
|
|
118
|
+
* 1. **Auto-namespace** — when one tool's `source` carries a `preset:<name>`
|
|
119
|
+
* prefix and the other doesn't, the preset tool is renamed to
|
|
120
|
+
* `<presetName>_<originalName>` (e.g. softDelete's `restore_post`
|
|
121
|
+
* becomes `softdelete_restore_post` when the user defines
|
|
122
|
+
* `actions.restore`). The user's tool wins the canonical name because
|
|
123
|
+
* it's the more-specific intent.
|
|
124
|
+
*
|
|
125
|
+
* 2. **Typed throw** — every other collision (two user actions, two
|
|
126
|
+
* custom routes, two presets) raises `ArcError('arc.mcp.tool_name_collision')`
|
|
127
|
+
* naming both sources so the host's stack trace points at the
|
|
128
|
+
* resource definitions, not the MCP SDK internals.
|
|
129
|
+
*
|
|
130
|
+
* Exported for the testing harness so collision behaviour can be exercised
|
|
131
|
+
* without a full server boot.
|
|
132
|
+
*/
|
|
133
|
+
declare function resolveToolCollisions(tools: readonly ToolDefinition[]): ToolDefinition[];
|
|
72
134
|
/** Minimal interface for McpServer — avoids hard SDK type dependency */
|
|
73
135
|
interface McpServerInstance {
|
|
74
136
|
connect: (transport: unknown) => Promise<void>;
|
|
@@ -516,4 +578,4 @@ declare function permissionDeniedResult(args: {
|
|
|
516
578
|
session: McpAuthResult | null;
|
|
517
579
|
}): CallToolResult;
|
|
518
580
|
//#endregion
|
|
519
|
-
export { type AuthRef, type BetterAuthHandler, type BuildMcpToolsFromBridgesOptions, type CallToolResult, type CreateMcpServerConfig, type CrudDescriptionMeta, type CrudDescriptionOverride, type CrudOperation, type DefinePromptConfig, type DefineToolConfig, type FieldRuleEntry, type FieldRulesToZodOptions, type InvokeControllerOptions, type McpAuthResolver, type McpAuthResult, type McpBridge, type McpGuard, type McpOperation, type McpPluginOptions, type McpResourceConfig, type McpServerInstance, type PromptDefinition, type PromptResult, type ResourceToToolsConfig, type ToolAnnotations, type ToolContext, type ToolDefinition, bridgeToMcp, buildMcpToolsFromBridges, buildRequestContext, createMcpServer, customGuard, defaultCrudDescription, definePrompt, defineTool, denied, fieldRulesToZod, filterResourcesForMcp, getOrgId, getUserId, guard, hasOrg, invokeController, isAuthenticated, isOrg, mcpHandlerAdapter, mcpPlugin, permissionDeniedResult, requireAuth, requireOrg, requireOrgId, requireRole, resolveCrudDescription, resourceToTools, toCallToolError, toCallToolResult, toCallToolSuccess };
|
|
581
|
+
export { type AuthRef, type BetterAuthHandler, type BuildMcpToolsFromBridgesOptions, type CallToolResult, type CreateMcpAuthFromBetterAuthApiKeyOptions, type CreateMcpServerConfig, type CrudDescriptionMeta, type CrudDescriptionOverride, type CrudOperation, type DefinePromptConfig, type DefineToolConfig, type FieldRuleEntry, type FieldRulesToZodOptions, type InvokeControllerOptions, type McpAuthResolver, type McpAuthResult, type McpBridge, type McpGuard, type McpOperation, type McpPluginOptions, type McpResourceConfig, type McpServerInstance, type PromptDefinition, type PromptResult, type ResourceToToolsConfig, type ToolAnnotations, type ToolContext, type ToolDefinition, bridgeToMcp, buildMcpToolsFromBridges, buildRequestContext, createMcpAuthFromBetterAuthApiKey, createMcpServer, customGuard, defaultCrudDescription, definePrompt, defineTool, denied, fieldRulesToZod, filterResourcesForMcp, getOrgId, getUserId, guard, hasOrg, invokeController, isAuthenticated, isOrg, mcpHandlerAdapter, mcpPlugin, permissionDeniedResult, requireAuth, requireOrg, requireOrgId, requireRole, resolveCrudDescription, resolveToolCollisions, resourceToTools, toCallToolError, toCallToolResult, toCallToolSuccess };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-
|
|
1
|
+
import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-C6FViZXw.mjs";
|
|
2
2
|
//#region src/integrations/mcp/defineTool.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define a type-safe MCP tool.
|
|
@@ -167,6 +167,97 @@ function buildMcpToolsFromBridges(bridges, options = {}) {
|
|
|
167
167
|
}).map(bridgeToMcp);
|
|
168
168
|
}
|
|
169
169
|
//#endregion
|
|
170
|
+
//#region src/integrations/mcp/betterAuthApiKey.ts
|
|
171
|
+
/**
|
|
172
|
+
* Build an {@link McpAuthResolver} that authenticates MCP requests against
|
|
173
|
+
* Better Auth's API-key plugin. See module docstring for the rationale.
|
|
174
|
+
*/
|
|
175
|
+
function createMcpAuthFromBetterAuthApiKey(auth, options = {}) {
|
|
176
|
+
const verifyApiKey = auth.api?.verifyApiKey;
|
|
177
|
+
if (typeof verifyApiKey !== "function") throw new Error("createMcpAuthFromBetterAuthApiKey: the provided Better Auth instance does not expose `api.verifyApiKey`. Register the `apiKey()` plugin on your auth instance (see https://www.better-auth.com/docs/plugins/api-key) and pass the same `auth` object you pass to your REST routes.");
|
|
178
|
+
const orgFromMetadata = options.orgFromMetadata ?? true;
|
|
179
|
+
const metadataOrgField = options.metadataOrgField ?? "organizationId";
|
|
180
|
+
const permissionScope = options.permissionScope === void 0 ? "mcp" : options.permissionScope;
|
|
181
|
+
return async (headers) => {
|
|
182
|
+
const apiKey = extractApiKey(headers);
|
|
183
|
+
if (!apiKey) return null;
|
|
184
|
+
let result;
|
|
185
|
+
try {
|
|
186
|
+
result = await verifyApiKey({
|
|
187
|
+
body: { key: apiKey },
|
|
188
|
+
headers
|
|
189
|
+
});
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
if (!result?.valid || !result.key) return null;
|
|
194
|
+
const key = result.key;
|
|
195
|
+
if (key.disabled === true) return null;
|
|
196
|
+
if (key.expiresAt) {
|
|
197
|
+
const exp = key.expiresAt instanceof Date ? key.expiresAt : new Date(key.expiresAt);
|
|
198
|
+
if (Number.isFinite(exp.getTime()) && exp.getTime() < Date.now()) return null;
|
|
199
|
+
}
|
|
200
|
+
const userId = key.userId ?? key.referenceId;
|
|
201
|
+
const organizationId = resolveOrg(key, orgFromMetadata, metadataOrgField);
|
|
202
|
+
const scopes = resolveScopes(key, permissionScope);
|
|
203
|
+
const clientId = !userId && key.id ? key.id : void 0;
|
|
204
|
+
if (!userId && !clientId) return null;
|
|
205
|
+
return {
|
|
206
|
+
...userId ? { userId } : {},
|
|
207
|
+
...organizationId ? { organizationId } : {},
|
|
208
|
+
...clientId ? { clientId } : {},
|
|
209
|
+
...scopes && scopes.length ? { scopes } : {}
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function extractApiKey(headers) {
|
|
214
|
+
const authz = headers.authorization ?? headers.Authorization;
|
|
215
|
+
if (typeof authz === "string" && authz.length > 0) {
|
|
216
|
+
const token = /^Bearer\s+(.+)$/i.exec(authz)?.[1];
|
|
217
|
+
if (token) return token.trim();
|
|
218
|
+
}
|
|
219
|
+
const direct = headers["x-api-key"] ?? headers["X-Api-Key"];
|
|
220
|
+
if (typeof direct === "string" && direct.length > 0) return direct.trim();
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
function resolveOrg(key, setting, field) {
|
|
224
|
+
if (setting === false) return void 0;
|
|
225
|
+
const metadata = parseMetadata(key.metadata);
|
|
226
|
+
if (typeof setting === "function") {
|
|
227
|
+
const value = setting(metadata, key);
|
|
228
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
229
|
+
}
|
|
230
|
+
if (metadata && typeof metadata === "object") {
|
|
231
|
+
const raw = metadata[field];
|
|
232
|
+
if (typeof raw === "string" && raw.length > 0) return raw;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function resolveScopes(key, scopeKey) {
|
|
236
|
+
if (scopeKey === false) return void 0;
|
|
237
|
+
const permissions = parseMetadata(key.permissions);
|
|
238
|
+
if (permissions && typeof permissions === "object") {
|
|
239
|
+
const bucket = permissions[scopeKey];
|
|
240
|
+
if (Array.isArray(bucket) && bucket.every((s) => typeof s === "string")) return bucket;
|
|
241
|
+
}
|
|
242
|
+
if (Array.isArray(key.scopes) && key.scopes.every((s) => typeof s === "string")) return key.scopes;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Better Auth stores `metadata` and `permissions` as JSON-stringified
|
|
246
|
+
* columns in SQL kits. Accept both shapes — already-parsed objects pass
|
|
247
|
+
* through, strings round-trip through `JSON.parse`. Failures yield
|
|
248
|
+
* `undefined` rather than throwing because malformed metadata should
|
|
249
|
+
* degrade gracefully (key still authenticates; org scope is absent).
|
|
250
|
+
*/
|
|
251
|
+
function parseMetadata(value) {
|
|
252
|
+
if (value === null || value === void 0) return void 0;
|
|
253
|
+
if (typeof value === "string") try {
|
|
254
|
+
return JSON.parse(value);
|
|
255
|
+
} catch {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
170
261
|
//#region src/integrations/mcp/definePrompt.ts
|
|
171
262
|
/**
|
|
172
263
|
* Define a type-safe MCP prompt.
|
|
@@ -184,4 +275,4 @@ function definePrompt(name, config) {
|
|
|
184
275
|
};
|
|
185
276
|
}
|
|
186
277
|
//#endregion
|
|
187
|
-
export { bridgeToMcp, buildMcpToolsFromBridges, buildRequestContext, createMcpServer, customGuard, defaultCrudDescription, definePrompt, defineTool, denied, fieldRulesToZod, filterResourcesForMcp, getOrgId, getUserId, guard, hasOrg, invokeController, isAuthenticated, isOrg, mcpHandlerAdapter, mcpPlugin, permissionDeniedResult, requireAuth, requireOrg, requireOrgId, requireRole, resolveCrudDescription, resourceToTools, toCallToolError, toCallToolResult, toCallToolSuccess };
|
|
278
|
+
export { bridgeToMcp, buildMcpToolsFromBridges, buildRequestContext, createMcpAuthFromBetterAuthApiKey, createMcpServer, customGuard, defaultCrudDescription, definePrompt, defineTool, denied, fieldRulesToZod, filterResourcesForMcp, getOrgId, getUserId, guard, hasOrg, invokeController, isAuthenticated, isOrg, mcpHandlerAdapter, mcpPlugin, permissionDeniedResult, requireAuth, requireOrg, requireOrgId, requireRole, resolveCrudDescription, resolveToolCollisions, resourceToTools, toCallToolError, toCallToolResult, toCallToolSuccess };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-
|
|
1
|
+
import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-C6FViZXw.mjs";
|
|
2
2
|
//#region src/integrations/mcp/testing.ts
|
|
3
3
|
/**
|
|
4
4
|
* @classytic/arc/mcp/testing — MCP Test Utilities
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sjson from "secure-json-parse";
|
|
1
2
|
import fp from "fastify-plugin";
|
|
2
3
|
//#region src/integrations/websocket/adapter.ts
|
|
3
4
|
/**
|
|
@@ -140,7 +141,7 @@ async function handleConnection(ctx, socket, request) {
|
|
|
140
141
|
}
|
|
141
142
|
let msg;
|
|
142
143
|
try {
|
|
143
|
-
msg =
|
|
144
|
+
msg = sjson.parse(typeof raw === "string" ? raw : raw.toString());
|
|
144
145
|
} catch {
|
|
145
146
|
socket.send(JSON.stringify({
|
|
146
147
|
type: "error",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { p as isArcError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
-
import { t as BaseController } from "./BaseController-
|
|
3
|
-
import { L as normalizePermissionResult } from "./permissions-
|
|
1
|
+
import { p as isArcError, t as ArcError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
+
import { t as BaseController } from "./BaseController-CgHomZ4d.mjs";
|
|
3
|
+
import { L as normalizePermissionResult } from "./permissions-B1Qy55fg.mjs";
|
|
4
4
|
import { t as executePipeline } from "./pipe-DiCyvyPN.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { f as resolvePipelineSteps } from "./routerShared-nCyJGOKX.mjs";
|
|
6
6
|
import { r as validateAggregations } from "./validate-DFIP2VrM.mjs";
|
|
7
7
|
import { n as executeAggregation } from "./buildHandler-C_SkBNF3.mjs";
|
|
8
8
|
import { t as pluralize } from "./pluralize-B9M8xvy-.mjs";
|
|
@@ -14,6 +14,31 @@ import fp from "fastify-plugin";
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
//#region src/integrations/mcp/createMcpServer.ts
|
|
16
16
|
/**
|
|
17
|
+
* @classytic/arc — createMcpServer()
|
|
18
|
+
*
|
|
19
|
+
* Level 2 — Declarative MCP server factory.
|
|
20
|
+
* Uses `server.registerTool()` / `server.registerPrompt()` (official SDK API)
|
|
21
|
+
* with flat inputSchema shapes — no z.object() wrapping, no Zod version hacks.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { createMcpServer, defineTool } from '@classytic/arc/mcp';
|
|
26
|
+
* import { z } from 'zod';
|
|
27
|
+
*
|
|
28
|
+
* const server = await createMcpServer({
|
|
29
|
+
* name: 'my-api',
|
|
30
|
+
* version: '1.0.0',
|
|
31
|
+
* tools: [
|
|
32
|
+
* defineTool('greet', {
|
|
33
|
+
* description: 'Say hello',
|
|
34
|
+
* input: { name: z.string() },
|
|
35
|
+
* handler: async ({ name }) => ({ content: [{ type: 'text', text: `Hello ${name}` }] }),
|
|
36
|
+
* }),
|
|
37
|
+
* ],
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
17
42
|
* Create a configured MCP server from declarative config.
|
|
18
43
|
*
|
|
19
44
|
* @param config - Server name, version, tools, prompts
|
|
@@ -25,11 +50,70 @@ async function createMcpServer(config, authRef) {
|
|
|
25
50
|
name: config.name,
|
|
26
51
|
version: config.version ?? "1.0.0"
|
|
27
52
|
}, config.instructions ? { instructions: config.instructions } : void 0);
|
|
28
|
-
if (config.tools)
|
|
53
|
+
if (config.tools) {
|
|
54
|
+
const resolved = resolveToolCollisions(config.tools);
|
|
55
|
+
for (const tool of resolved) registerTool(server, tool, authRef);
|
|
56
|
+
}
|
|
29
57
|
if (config.prompts) for (const prompt of config.prompts) registerPrompt(server, prompt);
|
|
30
58
|
return server;
|
|
31
59
|
}
|
|
32
60
|
/**
|
|
61
|
+
* Detect and resolve duplicate MCP tool names across the assembled tool list.
|
|
62
|
+
*
|
|
63
|
+
* Two outcomes:
|
|
64
|
+
*
|
|
65
|
+
* 1. **Auto-namespace** — when one tool's `source` carries a `preset:<name>`
|
|
66
|
+
* prefix and the other doesn't, the preset tool is renamed to
|
|
67
|
+
* `<presetName>_<originalName>` (e.g. softDelete's `restore_post`
|
|
68
|
+
* becomes `softdelete_restore_post` when the user defines
|
|
69
|
+
* `actions.restore`). The user's tool wins the canonical name because
|
|
70
|
+
* it's the more-specific intent.
|
|
71
|
+
*
|
|
72
|
+
* 2. **Typed throw** — every other collision (two user actions, two
|
|
73
|
+
* custom routes, two presets) raises `ArcError('arc.mcp.tool_name_collision')`
|
|
74
|
+
* naming both sources so the host's stack trace points at the
|
|
75
|
+
* resource definitions, not the MCP SDK internals.
|
|
76
|
+
*
|
|
77
|
+
* Exported for the testing harness so collision behaviour can be exercised
|
|
78
|
+
* without a full server boot.
|
|
79
|
+
*/
|
|
80
|
+
function resolveToolCollisions(tools) {
|
|
81
|
+
const byName = /* @__PURE__ */ new Map();
|
|
82
|
+
for (const t of tools) {
|
|
83
|
+
const list = byName.get(t.name);
|
|
84
|
+
if (list) list.push(t);
|
|
85
|
+
else byName.set(t.name, [t]);
|
|
86
|
+
}
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const [name, list] of byName) {
|
|
89
|
+
const [first, second, ...rest] = list;
|
|
90
|
+
if (!first) continue;
|
|
91
|
+
if (!second) {
|
|
92
|
+
out.push(first);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (rest.length === 0) {
|
|
96
|
+
const firstIsPreset = first.source?.startsWith("preset:") ?? false;
|
|
97
|
+
if (firstIsPreset !== (second.source?.startsWith("preset:") ?? false)) {
|
|
98
|
+
const preset = firstIsPreset ? first : second;
|
|
99
|
+
const user = firstIsPreset ? second : first;
|
|
100
|
+
const namespaced = `${(preset.source?.split(":")[1] ?? "preset").toLowerCase()}_${name}`;
|
|
101
|
+
out.push({
|
|
102
|
+
...preset,
|
|
103
|
+
name: namespaced
|
|
104
|
+
});
|
|
105
|
+
out.push(user);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
throw new ArcError(`MCP tool name '${name}' is registered by ${list.length} sources: ${list.map((t) => t.source ?? "(unknown)").join(", ")}. Rename one of the sources (e.g. via 'actions.${name}' → 'actions.${name}V2', or pass 'names: { [op]: "custom_name" }' in mcpPlugin overrides) so each tool has a unique name.`, {
|
|
110
|
+
code: "arc.mcp.tool_name_collision",
|
|
111
|
+
statusCode: 500
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
33
117
|
* Register a ToolDefinition using `server.registerTool()`.
|
|
34
118
|
*
|
|
35
119
|
* The inputSchema is passed as a flat Zod shape `{ field: z.string() }` —
|
|
@@ -223,6 +307,13 @@ function expandOperatorKeys(input) {
|
|
|
223
307
|
}
|
|
224
308
|
return out;
|
|
225
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Resolve an `McpAuthResult` (or `null` for unauthenticated calls) into a
|
|
312
|
+
* canonical `RequestScope`. Exported so the MCP tool helpers can build the
|
|
313
|
+
* same scope when constructing the permission-check context, ensuring
|
|
314
|
+
* `requireOrgMembership()` / `requireOrgRole()` see the same auth shape on
|
|
315
|
+
* MCP that they see on HTTP.
|
|
316
|
+
*/
|
|
226
317
|
function buildScope(auth) {
|
|
227
318
|
if (!auth) return { kind: "public" };
|
|
228
319
|
if (auth.clientId && auth.organizationId) return {
|
|
@@ -264,6 +355,7 @@ async function evaluatePermission(check, session, resource, action, input) {
|
|
|
264
355
|
_id: session.userId,
|
|
265
356
|
...session
|
|
266
357
|
} : null;
|
|
358
|
+
const scope = buildScope(session);
|
|
267
359
|
return normalizePermissionResult(await check({
|
|
268
360
|
user,
|
|
269
361
|
request: {
|
|
@@ -271,7 +363,9 @@ async function evaluatePermission(check, session, resource, action, input) {
|
|
|
271
363
|
headers: {},
|
|
272
364
|
params: {},
|
|
273
365
|
query: {},
|
|
274
|
-
body: input
|
|
366
|
+
body: input,
|
|
367
|
+
scope,
|
|
368
|
+
metadata: { _scope: scope }
|
|
275
369
|
},
|
|
276
370
|
resource,
|
|
277
371
|
action,
|
|
@@ -1473,7 +1567,8 @@ function resourceToTools(resource, config = {}) {
|
|
|
1473
1567
|
allowedOperators,
|
|
1474
1568
|
adapterBodies
|
|
1475
1569
|
}),
|
|
1476
|
-
handler: createCrudHandler(op, controller, resource.name, resource.permissions)
|
|
1570
|
+
handler: createCrudHandler(op, controller, resource.name, resource.permissions),
|
|
1571
|
+
source: `crud:${resource.name}:${op}`
|
|
1477
1572
|
});
|
|
1478
1573
|
}
|
|
1479
1574
|
}
|
|
@@ -1508,11 +1603,13 @@ function resourceToTools(resource, config = {}) {
|
|
|
1508
1603
|
for (const [key, val] of Object.entries(schemaIRToZodShape(ir))) if (!(key in inputShape)) inputShape[key] = val;
|
|
1509
1604
|
}
|
|
1510
1605
|
const toolName = prefix ? `${prefix}_${opName}_${resource.name}` : `${opName}_${resource.name}`;
|
|
1606
|
+
const routeSource = route._presetSource ? `preset:${route._presetSource}:${resource.name}:${opName}` : `route:${resource.name}:${route.method} ${route.path}`;
|
|
1511
1607
|
tools.push({
|
|
1512
1608
|
name: toolName,
|
|
1513
1609
|
description: toolDescription,
|
|
1514
1610
|
annotations: toolAnnotations,
|
|
1515
1611
|
inputSchema: inputShape,
|
|
1612
|
+
source: routeSource,
|
|
1516
1613
|
handler: mcpHandler ? createMcpHandlerPassthrough(mcpHandler) : createCustomRouteHandler({
|
|
1517
1614
|
handler: resolvedRouteHandler,
|
|
1518
1615
|
operation: route.operation,
|
|
@@ -1552,6 +1649,7 @@ function resourceToTools(resource, config = {}) {
|
|
|
1552
1649
|
description: String(description),
|
|
1553
1650
|
annotations,
|
|
1554
1651
|
inputSchema: inputShape,
|
|
1652
|
+
source: `action:${resource.name}:${actionName}`,
|
|
1555
1653
|
handler: createActionToolHandler(actionName, handler, actionPerms, resource.name, resource.permissions, rawSchema, idLess)
|
|
1556
1654
|
});
|
|
1557
1655
|
}
|
|
@@ -1799,6 +1897,11 @@ const mcpPluginImpl = async (fastify, options) => {
|
|
|
1799
1897
|
}
|
|
1800
1898
|
if (options.auth && isBetterAuth(options.auth)) await registerOAuthDiscovery(fastify, options.auth);
|
|
1801
1899
|
const prefix = options.prefix ?? "/mcp";
|
|
1900
|
+
if (options.auth === false) {
|
|
1901
|
+
const toolList = allTools.map((t) => t.name).slice(0, 5).join(", ");
|
|
1902
|
+
const more = allTools.length > 5 ? ` (+${allTools.length - 5} more)` : "";
|
|
1903
|
+
fastify.log.warn(`[arc/mcp] MCP server registered at "${prefix}" with auth DISABLED. Anyone who can reach this URL has full access to ${allTools.length} tools across ${enabledResources.length} resources: ${toolList}${more}. Pass \`auth: betterAuthInstance\` or \`auth: async (headers) => ...\` to gate access. Safe only for stdio transports, local development, or explicitly-public read-only APIs.`);
|
|
1904
|
+
}
|
|
1802
1905
|
fastify.get(`${prefix}/health`, async (_request, reply) => {
|
|
1803
1906
|
reply.send({
|
|
1804
1907
|
status: "ok",
|
|
@@ -2013,4 +2116,4 @@ const mcpPlugin = fp(mcpPluginImpl, {
|
|
|
2013
2116
|
fastify: "5.x"
|
|
2014
2117
|
});
|
|
2015
2118
|
//#endregion
|
|
2016
|
-
export { defaultCrudDescription as a, mcpHandlerAdapter as c, toCallToolResult as d, toCallToolSuccess as f, fieldRulesToZod as i, permissionDeniedResult as l, createMcpServer as m, mcpPlugin as n, resolveCrudDescription as o, buildRequestContext as p, resourceToTools as r, invokeController as s, filterResourcesForMcp as t, toCallToolError as u };
|
|
2119
|
+
export { defaultCrudDescription as a, mcpHandlerAdapter as c, toCallToolResult as d, toCallToolSuccess as f, resolveToolCollisions as h, fieldRulesToZod as i, permissionDeniedResult as l, createMcpServer as m, mcpPlugin as n, resolveCrudDescription as o, buildRequestContext as p, resourceToTools as r, invokeController as s, filterResourcesForMcp as t, toCallToolError as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as MiddlewareHandler, L as RequestWithExtras, Q as MiddlewareConfig } from "../index-
|
|
1
|
+
import { I as MiddlewareHandler, L as RequestWithExtras, Q as MiddlewareConfig } from "../index-C1JSBan-.mjs";
|
|
2
2
|
import { RouteHandlerMethod } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/middleware/middleware.d.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-TrJVIJl0.mjs";
|
|
2
|
-
import { t as multipartBody } from "../multipartBody-
|
|
2
|
+
import { t as multipartBody } from "../multipartBody-VjVWmZl8.mjs";
|
|
3
3
|
//#region src/middleware/middleware.ts
|
|
4
4
|
/**
|
|
5
5
|
* Named Middleware — Priority-based, conditional middleware execution.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sjson from "secure-json-parse";
|
|
1
2
|
//#region src/middleware/multipartBody.ts
|
|
2
3
|
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
3
4
|
const DEFAULT_MAX_FILES = 5;
|
|
@@ -118,7 +119,7 @@ function tryParseValue(value) {
|
|
|
118
119
|
if (Number.isFinite(num)) return num;
|
|
119
120
|
}
|
|
120
121
|
if (value.startsWith("{") && value.endsWith("}") || value.startsWith("[") && value.endsWith("]")) try {
|
|
121
|
-
return
|
|
122
|
+
return sjson.parse(value);
|
|
122
123
|
} catch {}
|
|
123
124
|
return value;
|
|
124
125
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as getUserRoles } from "./types-
|
|
1
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
2
2
|
import { n as convertRouteSchema } from "./schemaConverter-De34B1ZG.mjs";
|
|
3
3
|
import { t as pluralize } from "./pluralize-B9M8xvy-.mjs";
|
|
4
4
|
import { t as resolveActionPermission } from "./actionPermissions-Bjmvn7Eb.mjs";
|
|
5
|
-
import { t as buildActionBodySchema } from "./createActionRouter-
|
|
5
|
+
import { t as buildActionBodySchema } from "./createActionRouter-BHUMb1-y.mjs";
|
|
6
6
|
import fp from "fastify-plugin";
|
|
7
7
|
//#region src/docs/openapi/canonical-schemas.ts
|
|
8
8
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { n as normalizeRoles, t as getUserRoles } from "../types-
|
|
1
|
+
import { A as requireMandate, C as requireAuth, D as when, E as roles, F as applyPermissionResult, L as normalizePermissionResult, M as applyFieldWritePermissions, N as fields, O as requireAgentScope, P as resolveEffectiveRoles, S as not, 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, j as applyFieldReadPermissions, k as requireDPoP, l as createRoleHierarchy, 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-B1Qy55fg.mjs";
|
|
2
|
+
import { n as normalizeRoles, t as getUserRoles } from "../types-DV9WDfeg.mjs";
|
|
3
3
|
export { adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, applyPermissionResult, authenticated, createDynamicPermissionMatrix, createOrgPermissions, createRoleHierarchy, denyAll, fields, fullPublic, getUserRoles, normalizePermissionResult, normalizeRoles, not, ownerWithAdminBypass, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAgentScope, requireAuth, requireDPoP, requireMandate, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, resolveEffectiveRoles, roles, when };
|