@classytic/arc 2.18.2 → 2.18.4
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-DPwe89qs.mjs → BaseController-B-uXzf6I.mjs} +403 -216
- package/dist/{QueryCache-8YmAQ-zW.d.mts → QueryCache-Dsyv2mLS.d.mts} +1 -1
- package/dist/audit/index.d.mts +1 -1
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +4 -4
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-ClWxaceA.mjs → betterAuthOpenApi-DAAhMZff.mjs} +1 -1
- package/dist/{buildHandler-C_SkBNF3.mjs → buildHandler-Cvrc2p7r.mjs} +1 -1
- package/dist/cache/index.d.mts +3 -3
- package/dist/cache/index.mjs +3 -3
- 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.d.mts +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.d.mts +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-B41eZxyM.mjs → core-C88uIwd3.mjs} +161 -103
- package/dist/{createActionRouter-CRaIwB5S.mjs → createActionRouter-DGCCp-kP.mjs} +4 -2
- package/dist/{createAggregationRouter-n3O6WdLO.mjs → createAggregationRouter-Ca77b8iK.mjs} +3 -3
- package/dist/{createApp-DZvrb587.mjs → createApp-J1iPXY9p.mjs} +11 -11
- package/dist/{defineEvent-NPKUJRt8.mjs → defineEvent-DC981g-o.mjs} +1 -1
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-6mfTLBC-.mjs → elevation-BzhyVWfn.mjs} +1 -1
- package/dist/{eventPlugin-Dxp9JlKK.mjs → eventPlugin-Bw4bUDEO.mjs} +2 -2
- package/dist/{eventPlugin-DjEx_J3j.d.mts → eventPlugin-C1W1CRbf.d.mts} +1 -1
- package/dist/events/index.d.mts +8 -5
- package/dist/events/index.mjs +26 -7
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +52 -5
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +3 -3
- package/dist/{fastify-augmentation-CL48B6gW.d.mts → fastify-augmentation-BdCl_eNo.d.mts} +2 -2
- package/dist/{fields-DqZLp5d6.d.mts → fields-CKLrhyOb.d.mts} +1 -1
- package/dist/{generate-CcM24hoq.mjs → generate-BaxDdmJ8.mjs} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +2 -2
- package/dist/idempotency/index.mjs +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-CNiryUMN.d.mts → index-Cvo1u9n6.d.mts} +3 -3
- package/dist/{index-C000faom.d.mts → index-QYma8mdC.d.mts} +215 -241
- package/dist/{index-3L-adFJO.d.mts → index-vcWUGkwf.d.mts} +3 -3
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- package/dist/{init-DQ_rY1J7.mjs → init-DSsdRfk9.mjs} +3 -3
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.mjs +66 -1
- package/dist/integrations/mcp/index.d.mts +4 -4
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/streamline.d.mts +16 -1
- package/dist/integrations/streamline.mjs +33 -2
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +2 -2
- package/dist/{loadResourcesFromEntry-DCjYMgfC.mjs → loadResourcesFromEntry-XHNeUWpu.mjs} +1 -1
- package/dist/{mcpPlugin-BrepZ0cV.mjs → mcpPlugin-CzKdt3B8.mjs} +9 -9
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{openapi-CLZx9bwS.mjs → openapi-ButnRyOM.mjs} +5 -5
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-B1Qy55fg.mjs → permissions-3uzwkbQt.mjs} +99 -99
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/pipeline-D4sqiokR.mjs +336 -0
- package/dist/plugins/index.d.mts +5 -5
- package/dist/plugins/index.mjs +9 -9
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- 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 +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-BYbPobbF.mjs → presets-CF79RHEo.mjs} +1 -1
- package/dist/{queryCachePlugin-Do4P_WBC.d.mts → queryCachePlugin-Bd2tYgh5.d.mts} +2 -2
- package/dist/{queryCachePlugin-B4XMSSe7.mjs → queryCachePlugin-CnAeFqju.mjs} +3 -3
- package/dist/rateLimit-yKvxWAky.mjs +207 -0
- package/dist/{redis-cpqVb85w.d.mts → redis-3hqH_Qk_.d.mts} +1 -1
- package/dist/{redis-stream-BuiAS3aE.d.mts → redis-stream-BtEUtjxZ.d.mts} +5 -4
- package/dist/registerRoute-BEYn0ylk.mjs +108 -0
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{schemaIR-Bk6Tns82.mjs → schemaIR-CE6USIRi.mjs} +1 -1
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-jh5OqFjR.mjs → sse-BrY1W6U8.mjs} +11 -2
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +12 -2
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-DH-7WO64.d.mts → types-BWBcb88c.d.mts} +1 -1
- package/dist/{types-DEEWwQFR.d.mts → types-WVlAVp8T.d.mts} +10 -10
- package/dist/utils/index.d.mts +3 -3
- package/dist/utils/index.mjs +3 -3
- package/dist/{utils-BMQK8mRl.mjs → utils-xAQeDyU_.mjs} +1 -1
- package/dist/{validate-DFIP2VrM.mjs → validate-0__MuUsK.mjs} +201 -190
- package/dist/{versioning-5H4930tj.d.mts → versioning-BSEDGXKC.d.mts} +1 -1
- package/dist/{websocket-BZMUP7Gr.d.mts → websocket-BFBT5rH-.d.mts} +1 -1
- package/package.json +5 -5
- package/skills/arc/references/api-reference.md +1 -1
- package/dist/routerShared-Dl0A77Ba.mjs +0 -724
- /package/dist/{EventTransport-ClEDfiMS.mjs → EventTransport-7LPXLEUX.mjs} +0 -0
- /package/dist/{EventTransport-B1GFiYUy.d.mts → EventTransport-CaaXM5Y1.d.mts} +0 -0
- /package/dist/{HookSystem-Cmf7-Etp.mjs → HookSystem-BfCpWbwM.mjs} +0 -0
- /package/dist/{ResourceRegistry-CzSzVK1m.mjs → ResourceRegistry-CWmOggxS.mjs} +0 -0
- /package/dist/{actionPermissions-Bjmvn7Eb.mjs → actionPermissions-CX9QIMTd.mjs} +0 -0
- /package/dist/{caching-8MfrrUYU.mjs → caching-C0DdKiYB.mjs} +0 -0
- /package/dist/{elevation-DvwtjDwu.d.mts → elevation-DW90z5bT.d.mts} +0 -0
- /package/dist/{errorHandler-wQejMBT3.mjs → errorHandler-Bua97SEt.mjs} +0 -0
- /package/dist/{errorHandler-DuqTqeqn.d.mts → errorHandler-_Lp2k9M3.d.mts} +0 -0
- /package/dist/{errors-4ZloYFvm.d.mts → errors-DZOemeGa.d.mts} +0 -0
- /package/dist/{externalPaths-ClykOQqa.d.mts → externalPaths-DLle0R07.d.mts} +0 -0
- /package/dist/{generate-cRnsafMS.d.mts → generate-Dmk8cfvX.d.mts} +0 -0
- /package/dist/{init-D-vrTZ1G.d.mts → init-BS4Daqnv.d.mts} +0 -0
- /package/dist/{interface-DoE6UoC2.d.mts → interface-CsrGR8LF.d.mts} +0 -0
- /package/dist/{interface-BVkZsvEe.d.mts → interface-DL7LlWS5.d.mts} +0 -0
- /package/dist/{keys-DopsCuyQ.mjs → keys-B3yyBEyH.mjs} +0 -0
- /package/dist/{loadResources-fol6JuvW.mjs → loadResources-CwXt9MCh.mjs} +0 -0
- /package/dist/{memory-QOLe11D5.mjs → memory-TdTHCS9-.mjs} +0 -0
- /package/dist/{metrics-B-PU4-Yu.mjs → metrics-Da2EUPi9.mjs} +0 -0
- /package/dist/{multipartBody-VjVWmZl8.mjs → multipartBody-709YcXi1.mjs} +0 -0
- /package/dist/{pipe-DiCyvyPN.mjs → pipe-9Tl-OAus.mjs} +0 -0
- /package/dist/{pluralize-B9M8xvy-.mjs → pluralize-BxdUl2P4.mjs} +0 -0
- /package/dist/{preloadResources-BOXzPhKU.mjs → preloadResources-B-j-iqo-.mjs} +0 -0
- /package/dist/{pushref-registry-Bq_-EVsC.d.mts → pushref-registry-Dqclzjy7.d.mts} +0 -0
- /package/dist/{registry-BrlHkrRS.mjs → registry-Br15xdGM.mjs} +0 -0
- /package/dist/{replyHelpers-boS5Rx26.mjs → replyHelpers-DXHVxrrj.mjs} +0 -0
- /package/dist/{schemaConverter-De34B1ZG.mjs → schemaConverter-DatAaVNj.mjs} +0 -0
- /package/dist/{sessionManager-DlmaHUAl.d.mts → sessionManager-CyHpYXoB.d.mts} +0 -0
- /package/dist/{storage-BFmamN5m.d.mts → storage-C5q8fuEt.d.mts} +0 -0
- /package/dist/{store-helpers-5JgaxieH.mjs → store-helpers-DjSYI2uP.mjs} +0 -0
- /package/dist/{tracing-BYQ3hsbM.d.mts → tracing-Bnb1-YVd.d.mts} +0 -0
- /package/dist/{typeGuards-BzkXkvVv.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
- /package/dist/{types-CfapW2t8.d.mts → types-BJwJN2Pg.d.mts} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{versioning-CuIwUQZ2.mjs → versioning-C0K8KQLx.mjs} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as WebSocketClient, l as WebSocketMessage, u as WebSocketPluginOptions } from "../websocket-
|
|
1
|
+
import { c as WebSocketClient, l as WebSocketMessage, u as WebSocketPluginOptions } from "../websocket-BFBT5rH-.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 McpAuthResult, d as PromptDefinition, g as ToolDefinition, h as ToolContext, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpResourceConfig } from "../types-
|
|
4
|
+
import { c as McpAuthResult, d as PromptDefinition, g as ToolDefinition, h as ToolContext, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpResourceConfig } from "../types-BWBcb88c.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 };
|
|
@@ -40,6 +40,68 @@ var Semaphore = class {
|
|
|
40
40
|
else this.available++;
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Whether an existing schedule matches the currently configured repeat spec.
|
|
45
|
+
*
|
|
46
|
+
* BullMQ keys repeatable schedules by `name:jobId:endDate:tz:(pattern|every)`
|
|
47
|
+
* — exactly these fields determine schedule identity, so they are exactly
|
|
48
|
+
* what we compare. `limit` is intentionally NOT compared: it isn't part of
|
|
49
|
+
* the key, so a changed limit upserts in place and never strands a stale
|
|
50
|
+
* schedule.
|
|
51
|
+
*/
|
|
52
|
+
function repeatSpecMatches(record, spec) {
|
|
53
|
+
if ((record.pattern ?? void 0) !== spec.pattern) return false;
|
|
54
|
+
if ((record.every == null ? void 0 : Number(record.every)) !== spec.every) return false;
|
|
55
|
+
if ((record.tz ?? void 0) !== spec.tz) return false;
|
|
56
|
+
if ((record.endDate == null ? void 0 : record.endDate) !== (spec.endDate == null ? void 0 : new Date(spec.endDate).getTime())) return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Remove repeatable schedules left behind by a previous deploy whose repeat
|
|
61
|
+
* spec no longer matches the current `JobDefinition.repeat`.
|
|
62
|
+
*
|
|
63
|
+
* BullMQ keys repeatable schedules by a hash of their repeat options, so
|
|
64
|
+
* re-adding a job with a CHANGED pattern/every/tz/endDate creates a NEW
|
|
65
|
+
* schedule while the old one keeps firing — the job double-fires after every
|
|
66
|
+
* repeat-config change. This reconciles at plugin registration:
|
|
67
|
+
*
|
|
68
|
+
* - Only schedulers whose `name` equals the registered job's name are
|
|
69
|
+
* candidates — foreign/unknown schedulers are never touched.
|
|
70
|
+
* - A scheduler matching the current spec is kept (the subsequent
|
|
71
|
+
* `queue.add` upserts it in place, preserving its timing state).
|
|
72
|
+
* - Fail-open: any error logs a warning and never blocks boot — this is
|
|
73
|
+
* boot-time hygiene, not a correctness gate.
|
|
74
|
+
*
|
|
75
|
+
* Uses the modern `getJobSchedulers()` / `removeJobScheduler()` API (BullMQ
|
|
76
|
+
* 5.x job schedulers) and falls back to the deprecated `getRepeatableJobs()`
|
|
77
|
+
* / `removeRepeatableByKey()` pair on older clients. When neither API exists
|
|
78
|
+
* the step is skipped entirely.
|
|
79
|
+
*/
|
|
80
|
+
async function removeStaleRepeatSchedulers(queue, jobName, spec, log) {
|
|
81
|
+
const q = queue;
|
|
82
|
+
const list = typeof q.getJobSchedulers === "function" ? q.getJobSchedulers.bind(q) : typeof q.getRepeatableJobs === "function" ? q.getRepeatableJobs.bind(q) : null;
|
|
83
|
+
const remove = typeof q.removeJobScheduler === "function" ? q.removeJobScheduler.bind(q) : typeof q.removeRepeatableByKey === "function" ? q.removeRepeatableByKey.bind(q) : null;
|
|
84
|
+
if (!list || !remove) return;
|
|
85
|
+
try {
|
|
86
|
+
const existing = await list();
|
|
87
|
+
for (const record of existing) {
|
|
88
|
+
if (!record || record.name !== jobName) continue;
|
|
89
|
+
if (repeatSpecMatches(record, spec)) continue;
|
|
90
|
+
await remove(record.key);
|
|
91
|
+
log.warn({
|
|
92
|
+
job: jobName,
|
|
93
|
+
schedulerKey: record.key,
|
|
94
|
+
stalePattern: record.pattern ?? void 0,
|
|
95
|
+
staleEvery: record.every ?? void 0
|
|
96
|
+
}, `[arc/jobs] Removed stale repeatable schedule for '${jobName}' — repeat config changed since last deploy`);
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
log.warn({
|
|
100
|
+
err,
|
|
101
|
+
job: jobName
|
|
102
|
+
}, `[arc/jobs] Failed to reconcile repeatable schedules for '${jobName}' — continuing (boot-time hygiene only)`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
43
105
|
const jobsPluginImpl = async (fastify, options) => {
|
|
44
106
|
const { connection, jobs, prefix = "/jobs", bridgeEvents = true, defaults = {} } = options;
|
|
45
107
|
let Queue;
|
|
@@ -78,6 +140,7 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
78
140
|
...job.repeat.endDate ? { endDate: job.repeat.endDate } : {},
|
|
79
141
|
...job.repeat.limit != null ? { limit: job.repeat.limit } : {}
|
|
80
142
|
};
|
|
143
|
+
await removeStaleRepeatSchedulers(queue, queueName, repeatOpts, fastify.log);
|
|
81
144
|
await queue.add(queueName, {}, {
|
|
82
145
|
repeat: repeatOpts,
|
|
83
146
|
removeOnComplete: defaults.removeOnComplete ?? 100,
|
|
@@ -193,6 +256,8 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
193
256
|
workers.set(queueName, worker);
|
|
194
257
|
}
|
|
195
258
|
const JOB_PAYLOAD_WARN_BYTES = 100 * 1024;
|
|
259
|
+
const jobDefsByName = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const job of jobs) if (!jobDefsByName.has(job.name)) jobDefsByName.set(job.name, job);
|
|
196
261
|
const dispatcher = {
|
|
197
262
|
async dispatch(name, data, opts = {}) {
|
|
198
263
|
const queue = queues.get(name);
|
|
@@ -205,7 +270,7 @@ const jobsPluginImpl = async (fastify, options) => {
|
|
|
205
270
|
limit: JOB_PAYLOAD_WARN_BYTES
|
|
206
271
|
}, `[arc/jobs] Large job payload — prefer passing IDs and reloading in the handler`);
|
|
207
272
|
} catch {}
|
|
208
|
-
const jobDef =
|
|
273
|
+
const jobDef = jobDefsByName.get(name);
|
|
209
274
|
return { jobId: (await queue.add(name, data, {
|
|
210
275
|
delay: opts.delay,
|
|
211
276
|
priority: opts.priority,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { V as ResourceDefinition, bt as IRequestContext, yt as IControllerResponse } from "../../index-
|
|
2
|
-
import { i as RequestScope } from "../../types-
|
|
3
|
-
import { c as PermissionCheck } from "../../fields-
|
|
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-
|
|
1
|
+
import { V as ResourceDefinition, bt as IRequestContext, yt as IControllerResponse } from "../../index-QYma8mdC.mjs";
|
|
2
|
+
import { i as RequestScope } from "../../types-BJwJN2Pg.mjs";
|
|
3
|
+
import { c as PermissionCheck } from "../../fields-CKLrhyOb.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-BWBcb88c.mjs";
|
|
5
5
|
import { ErrorContract } from "@classytic/repo-core/errors";
|
|
6
6
|
import { FastifyPluginAsync } from "fastify";
|
|
7
7
|
import { z } from "zod";
|
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-CzKdt3B8.mjs";
|
|
2
2
|
//#region src/integrations/mcp/defineTool.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define a type-safe MCP tool.
|
|
@@ -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-CzKdt3B8.mjs";
|
|
2
2
|
//#region src/integrations/mcp/testing.ts
|
|
3
3
|
/**
|
|
4
4
|
* @classytic/arc/mcp/testing — MCP Test Utilities
|
|
@@ -232,6 +232,21 @@ interface StreamlinePluginOptions {
|
|
|
232
232
|
* because streamline extended its bus.
|
|
233
233
|
*/
|
|
234
234
|
declare const STREAMLINE_BUS_EVENTS: readonly ["step:started", "step:completed", "step:failed", "step:waiting", "step:skipped", "step:retry-scheduled", "step:compensated", "workflow:started", "workflow:completed", "workflow:failed", "workflow:waiting", "workflow:resumed", "workflow:cancelled", "workflow:recovered", "workflow:retry", "workflow:compensating", "engine:error", "scheduler:error", "scheduler:circuit-open"];
|
|
235
|
+
/**
|
|
236
|
+
* Non-durable streaming frames from streamline >= 2.6's `ctx.stream(frame)`
|
|
237
|
+
* (LLM tokens, percent-complete, live previews).
|
|
238
|
+
*
|
|
239
|
+
* Delivered on the SSE endpoint ONLY — deliberately excluded from
|
|
240
|
+
* `STREAMLINE_BUS_EVENTS` / `bridgeBusEvents` because frames are
|
|
241
|
+
* high-frequency, at-most-once UI traffic, not domain events: republishing
|
|
242
|
+
* every token onto arc's Redis/Kafka transport would flood it. Subscribers
|
|
243
|
+
* that genuinely want frames on the transport can bridge
|
|
244
|
+
* `streamline:step.stream` from streamline's own arc-shape transport.
|
|
245
|
+
*
|
|
246
|
+
* On streamline < 2.6 the subscription is a structural no-op (the bus never
|
|
247
|
+
* emits the event) — no version gate needed.
|
|
248
|
+
*/
|
|
249
|
+
declare const STREAMLINE_STREAM_EVENTS: readonly ["step:stream"];
|
|
235
250
|
/**
|
|
236
251
|
* Workflow events that should auto-close an SSE stream when observed.
|
|
237
252
|
* Recovered / waiting / resumed / retry / compensating are NOT terminal —
|
|
@@ -255,4 +270,4 @@ declare const TERMINAL_RUN_STATUSES: Set<string>;
|
|
|
255
270
|
*/
|
|
256
271
|
declare const streamlinePlugin: FastifyPluginAsync<StreamlinePluginOptions>;
|
|
257
272
|
//#endregion
|
|
258
|
-
export { STREAMLINE_BUS_EVENTS, STREAMLINE_FAILURE_CODES, STREAMLINE_TERMINAL_EVENTS, StreamlinePluginOptions, TERMINAL_RUN_STATUSES, WorkflowLike, WorkflowRunLike, WorkflowStartOptions, streamlinePlugin };
|
|
273
|
+
export { STREAMLINE_BUS_EVENTS, STREAMLINE_FAILURE_CODES, STREAMLINE_STREAM_EVENTS, STREAMLINE_TERMINAL_EVENTS, StreamlinePluginOptions, TERMINAL_RUN_STATUSES, WorkflowLike, WorkflowRunLike, WorkflowStartOptions, streamlinePlugin };
|
|
@@ -49,6 +49,21 @@ const STREAMLINE_BUS_EVENTS = [
|
|
|
49
49
|
"scheduler:circuit-open"
|
|
50
50
|
];
|
|
51
51
|
/**
|
|
52
|
+
* Non-durable streaming frames from streamline >= 2.6's `ctx.stream(frame)`
|
|
53
|
+
* (LLM tokens, percent-complete, live previews).
|
|
54
|
+
*
|
|
55
|
+
* Delivered on the SSE endpoint ONLY — deliberately excluded from
|
|
56
|
+
* `STREAMLINE_BUS_EVENTS` / `bridgeBusEvents` because frames are
|
|
57
|
+
* high-frequency, at-most-once UI traffic, not domain events: republishing
|
|
58
|
+
* every token onto arc's Redis/Kafka transport would flood it. Subscribers
|
|
59
|
+
* that genuinely want frames on the transport can bridge
|
|
60
|
+
* `streamline:step.stream` from streamline's own arc-shape transport.
|
|
61
|
+
*
|
|
62
|
+
* On streamline < 2.6 the subscription is a structural no-op (the bus never
|
|
63
|
+
* emits the event) — no version gate needed.
|
|
64
|
+
*/
|
|
65
|
+
const STREAMLINE_STREAM_EVENTS = ["step:stream"];
|
|
66
|
+
/**
|
|
52
67
|
* Workflow events that should auto-close an SSE stream when observed.
|
|
53
68
|
* Recovered / waiting / resumed / retry / compensating are NOT terminal —
|
|
54
69
|
* the run is still active after them.
|
|
@@ -99,6 +114,14 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
99
114
|
};
|
|
100
115
|
for (const [id, wf] of registry) {
|
|
101
116
|
const routePrefix = `${routeScope}/${id}`;
|
|
117
|
+
const visibilityRepo = wf.container?.repository;
|
|
118
|
+
const visibilityGetById = visibilityRepo?.getById?.bind(visibilityRepo);
|
|
119
|
+
const assertRunVisible = async (request, runId) => {
|
|
120
|
+
if (!visibilityGetById) return;
|
|
121
|
+
const tenantOpts = resolveTenantOpts(request);
|
|
122
|
+
if (tenantOpts.bypassTenant || tenantOpts.tenantId === void 0) return;
|
|
123
|
+
if (!await visibilityGetById(runId, { tenantId: tenantOpts.tenantId })) throw new NotFoundError("Workflow run", runId);
|
|
124
|
+
};
|
|
102
125
|
fastify.post(`${routePrefix}/start`, { preHandler: authPreHandler }, async (request, reply) => {
|
|
103
126
|
if (!await checkPerm("start", request)) throw new ForbiddenError();
|
|
104
127
|
const body = request.body;
|
|
@@ -170,6 +193,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
170
193
|
fastify.get(`${routePrefix}/runs/:runId`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
171
194
|
if (!await checkPerm("get", request)) throw new ForbiddenError();
|
|
172
195
|
const { runId } = request.params;
|
|
196
|
+
await assertRunVisible(request, runId);
|
|
173
197
|
const run = await wf.get(runId);
|
|
174
198
|
if (!run) throw new NotFoundError("Workflow run", runId);
|
|
175
199
|
return run;
|
|
@@ -177,6 +201,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
177
201
|
fastify.post(`${routePrefix}/runs/:runId/resume`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
178
202
|
if (!await checkPerm("resume", request)) throw new ForbiddenError();
|
|
179
203
|
const { runId } = request.params;
|
|
204
|
+
await assertRunVisible(request, runId);
|
|
180
205
|
const { payload } = request.body ?? {};
|
|
181
206
|
const run = await wf.resume(runId, payload);
|
|
182
207
|
if (bridgeEvents && fastify.events?.publish) try {
|
|
@@ -196,6 +221,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
196
221
|
fastify.post(`${routePrefix}/runs/:runId/cancel`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
197
222
|
if (!await checkPerm("cancel", request)) throw new ForbiddenError();
|
|
198
223
|
const { runId } = request.params;
|
|
224
|
+
await assertRunVisible(request, runId);
|
|
199
225
|
const run = await wf.cancel(runId);
|
|
200
226
|
if (bridgeEvents && fastify.events?.publish) try {
|
|
201
227
|
await fastify.events.publish(`workflow.${id}.cancelled`, {
|
|
@@ -212,6 +238,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
212
238
|
});
|
|
213
239
|
fastify.post(`${routePrefix}/runs/:runId/execute`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
214
240
|
const { runId } = request.params;
|
|
241
|
+
await assertRunVisible(request, runId);
|
|
215
242
|
return await wf.engine.execute(runId);
|
|
216
243
|
});
|
|
217
244
|
const deleteRepo = wf.container?.repository;
|
|
@@ -235,16 +262,19 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
235
262
|
if (wf.engine.waitFor) fastify.get(`${routePrefix}/runs/:runId/wait`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
236
263
|
if (!await checkPerm("get", request)) throw new ForbiddenError();
|
|
237
264
|
const { runId } = request.params;
|
|
265
|
+
await assertRunVisible(request, runId);
|
|
238
266
|
const { timeout } = request.query ?? {};
|
|
239
267
|
const timeoutMs = timeout ? Number.parseInt(timeout, 10) : 3e4;
|
|
240
268
|
return await wf.engine.waitFor?.(runId, { timeout: Math.min(timeoutMs, 12e4) });
|
|
241
269
|
});
|
|
242
270
|
if (wf.engine.pause) fastify.post(`${routePrefix}/runs/:runId/pause`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
243
271
|
const { runId } = request.params;
|
|
272
|
+
await assertRunVisible(request, runId);
|
|
244
273
|
return await wf.engine.pause?.(runId);
|
|
245
274
|
});
|
|
246
275
|
if (wf.engine.rewindTo) fastify.post(`${routePrefix}/runs/:runId/rewind`, { preHandler: authPreHandler }, async (request, _reply) => {
|
|
247
276
|
const { runId } = request.params;
|
|
277
|
+
await assertRunVisible(request, runId);
|
|
248
278
|
const { stepId } = request.body ?? {};
|
|
249
279
|
if (!stepId) throw createError(400, "stepId is required");
|
|
250
280
|
return await wf.engine.rewindTo?.(runId, stepId);
|
|
@@ -266,6 +296,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
266
296
|
if (enableStreaming && wf.container?.eventBus) fastify.get(`${routePrefix}/runs/:runId/stream`, { preHandler: authPreHandler }, async (request, reply) => {
|
|
267
297
|
if (!await checkPerm("get", request)) throw new ForbiddenError();
|
|
268
298
|
const { runId } = request.params;
|
|
299
|
+
await assertRunVisible(request, runId);
|
|
269
300
|
const run = await wf.get(runId);
|
|
270
301
|
if (!run) throw new NotFoundError("Workflow run", runId);
|
|
271
302
|
reply.raw.writeHead(200, {
|
|
@@ -312,7 +343,7 @@ const streamlinePluginImpl = async (fastify, options) => {
|
|
|
312
343
|
}
|
|
313
344
|
}, 15e3);
|
|
314
345
|
heartbeat.unref?.();
|
|
315
|
-
for (const eventName of STREAMLINE_BUS_EVENTS) {
|
|
346
|
+
for (const eventName of [...STREAMLINE_BUS_EVENTS, ...STREAMLINE_STREAM_EVENTS]) {
|
|
316
347
|
const fn = (payload) => {
|
|
317
348
|
const p = payload;
|
|
318
349
|
if (typeof p?.runId === "string" && p.runId !== runId) return;
|
|
@@ -367,4 +398,4 @@ const streamlinePlugin = fp(streamlinePluginImpl, {
|
|
|
367
398
|
fastify: "5.x"
|
|
368
399
|
});
|
|
369
400
|
//#endregion
|
|
370
|
-
export { STREAMLINE_BUS_EVENTS, STREAMLINE_FAILURE_CODES, STREAMLINE_TERMINAL_EVENTS, TERMINAL_RUN_STATUSES, streamlinePlugin };
|
|
401
|
+
export { STREAMLINE_BUS_EVENTS, STREAMLINE_FAILURE_CODES, STREAMLINE_STREAM_EVENTS, STREAMLINE_TERMINAL_EVENTS, TERMINAL_RUN_STATUSES, streamlinePlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as SerializedEntry, i as Principal, n as FencedWriteOutcome, s as PushRefStore, t as EnvelopeSnapshot } from "../pushref-registry-
|
|
1
|
+
import { c as SerializedEntry, i as Principal, n as FencedWriteOutcome, s as PushRefStore, t as EnvelopeSnapshot } from "../pushref-registry-Dqclzjy7.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/integrations/websocket-pushref-redis.d.ts
|
|
4
4
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as GeoRoomOptions, c as WebSocketClient, d as LocalWebSocketAdapter, f as WebSocketAdapter, i as GeoRoom, l as WebSocketMessage, n as GeoPoint, o as RoomManager, r as GeoPublishOpts, s as AuthResult, t as websocketPlugin, u as WebSocketPluginOptions } from "../websocket-
|
|
2
|
-
import { a as PushRefRegistry, c as SerializedEntry, i as Principal, o as PushRefRegistryOptions, r as MemoryPushRefStore, s as PushRefStore } from "../pushref-registry-
|
|
1
|
+
import { a as GeoRoomOptions, c as WebSocketClient, d as LocalWebSocketAdapter, f as WebSocketAdapter, i as GeoRoom, l as WebSocketMessage, n as GeoPoint, o as RoomManager, r as GeoPublishOpts, s as AuthResult, t as websocketPlugin, u as WebSocketPluginOptions } from "../websocket-BFBT5rH-.mjs";
|
|
2
|
+
import { a as PushRefRegistry, c as SerializedEntry, i as Principal, o as PushRefRegistryOptions, r as MemoryPushRefStore, s as PushRefStore } from "../pushref-registry-Dqclzjy7.mjs";
|
|
3
3
|
export { AuthResult, GeoPoint, GeoPublishOpts, GeoRoom, GeoRoomOptions, LocalWebSocketAdapter, MemoryPushRefStore, Principal, PushRefRegistry, PushRefRegistryOptions, PushRefStore, RoomManager, SerializedEntry, WebSocketAdapter, WebSocketClient, WebSocketMessage, WebSocketPluginOptions, websocketPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ResourceRegistry } from "./ResourceRegistry-
|
|
1
|
+
import { t as ResourceRegistry } from "./ResourceRegistry-CWmOggxS.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
//#region src/cli/utils/loadResourcesFromEntry.ts
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { p as isArcError, t as ArcError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
-
import { t as BaseController } from "./BaseController-
|
|
3
|
-
import {
|
|
4
|
-
import { t as executePipeline } from "./pipe-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as executeAggregation } from "./buildHandler-
|
|
8
|
-
import { t as pluralize } from "./pluralize-
|
|
9
|
-
import { t as resolveActionPermission } from "./actionPermissions-
|
|
10
|
-
import { i as shouldRejectAdditionalProperties, r as schemaIRToZodShape, t as normalizeSchemaIR } from "./schemaIR-
|
|
2
|
+
import { t as BaseController } from "./BaseController-B-uXzf6I.mjs";
|
|
3
|
+
import { N as normalizePermissionResult } from "./permissions-3uzwkbQt.mjs";
|
|
4
|
+
import { t as executePipeline } from "./pipe-9Tl-OAus.mjs";
|
|
5
|
+
import { r as resolvePipelineSteps } from "./pipeline-D4sqiokR.mjs";
|
|
6
|
+
import { n as validateAggregations } from "./validate-0__MuUsK.mjs";
|
|
7
|
+
import { n as executeAggregation } from "./buildHandler-Cvrc2p7r.mjs";
|
|
8
|
+
import { t as pluralize } from "./pluralize-BxdUl2P4.mjs";
|
|
9
|
+
import { t as resolveActionPermission } from "./actionPermissions-CX9QIMTd.mjs";
|
|
10
|
+
import { i as shouldRejectAdditionalProperties, r as schemaIRToZodShape, t as normalizeSchemaIR } from "./schemaIR-CE6USIRi.mjs";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { isHttpError, toErrorContract } from "@classytic/repo-core/errors";
|
|
13
13
|
import fp from "fastify-plugin";
|
|
@@ -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-QYma8mdC.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-709YcXi1.mjs";
|
|
3
3
|
//#region src/middleware/middleware.ts
|
|
4
4
|
/**
|
|
5
5
|
* Named Middleware — Priority-based, conditional middleware execution.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as getUserRoles } from "./types-
|
|
2
|
-
import { n as convertRouteSchema } from "./schemaConverter-
|
|
3
|
-
import { t as pluralize } from "./pluralize-
|
|
4
|
-
import { t as resolveActionPermission } from "./actionPermissions-
|
|
5
|
-
import { t as buildActionBodySchema } from "./createActionRouter-
|
|
1
|
+
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
2
|
+
import { n as convertRouteSchema } from "./schemaConverter-DatAaVNj.mjs";
|
|
3
|
+
import { t as pluralize } from "./pluralize-BxdUl2P4.mjs";
|
|
4
|
+
import { t as resolveActionPermission } from "./actionPermissions-CX9QIMTd.mjs";
|
|
5
|
+
import { t as buildActionBodySchema } from "./createActionRouter-DGCCp-kP.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 applyFieldWritePermissions, c as PermissionCheck, d as UserBase, f as getUserRoles, i as applyFieldReadPermissions, l as PermissionContext, n as FieldPermissionMap, o as fields, p as normalizeRoles, r as FieldPermissionType, s as resolveEffectiveRoles, t as FieldPermission, u as PermissionResult } from "../fields-
|
|
2
|
-
import { A as requireRoles, C as allOf, D as not, E as denyAll, F as RequireAgentScopeOptions, I as RequireMandateOptions, L as requireAgentScope, M as when, N as applyPermissionResult, O as requireAuth, P as normalizePermissionResult, R as requireDPoP, S as createOrgPermissions, T as anyOf, _ as ConnectEventsOptions, a as presets_d_exports, b as PermissionEventBus, c as readOnly, d as requireOrgRole, f as requireScopeContext, g as createRoleHierarchy, h as RoleHierarchy, i as ownerWithAdminBypass, j as roles, 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, z as requireMandate } from "../index-
|
|
1
|
+
import { a as applyFieldWritePermissions, c as PermissionCheck, d as UserBase, f as getUserRoles, i as applyFieldReadPermissions, l as PermissionContext, n as FieldPermissionMap, o as fields, p as normalizeRoles, r as FieldPermissionType, s as resolveEffectiveRoles, t as FieldPermission, u as PermissionResult } from "../fields-CKLrhyOb.mjs";
|
|
2
|
+
import { A as requireRoles, C as allOf, D as not, E as denyAll, F as RequireAgentScopeOptions, I as RequireMandateOptions, L as requireAgentScope, M as when, N as applyPermissionResult, O as requireAuth, P as normalizePermissionResult, R as requireDPoP, S as createOrgPermissions, T as anyOf, _ as ConnectEventsOptions, a as presets_d_exports, b as PermissionEventBus, c as readOnly, d as requireOrgRole, f as requireScopeContext, g as createRoleHierarchy, h as RoleHierarchy, i as ownerWithAdminBypass, j as roles, 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, z as requireMandate } from "../index-vcWUGkwf.mjs";
|
|
3
3
|
export { ConnectEventsOptions, DynamicPermissionMatrix, DynamicPermissionMatrixConfig, FieldPermission, FieldPermissionMap, FieldPermissionType, PermissionCheck, PermissionContext, PermissionEventBus, PermissionResult, RequireAgentScopeOptions, RequireMandateOptions, RoleHierarchy, UserBase, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, applyPermissionResult, authenticated, createDynamicPermissionMatrix, createOrgPermissions, createRoleHierarchy, denyAll, fields, fullPublic, getUserRoles, normalizePermissionResult, normalizeRoles, not, ownerWithAdminBypass, presets_d_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAgentScope, requireAuth, requireDPoP, requireMandate, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, resolveEffectiveRoles, roles, when };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as requireMandate, C as requireAuth, D as when, E as roles, F 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 applyFieldWritePermissions, I as fields, L as resolveEffectiveRoles, N as normalizePermissionResult, O as requireAgentScope, P as applyFieldReadPermissions, 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 applyPermissionResult, 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-3uzwkbQt.mjs";
|
|
2
|
+
import { n as normalizeRoles, t as getUserRoles } from "../types-D57iXYb8.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 };
|
|
@@ -1,104 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { C as isService, S as isOrgInScope, a as getDPoPJkt, b as isElevated, d as getScopeContext, f as getScopeContextMap, g as getUserId, m as getTeamId, o as getMandate, p as getServiceScopes, u as getRequestScope, v as hasOrgAccess, x as isMember } from "./types-Bi0r0vjG.mjs";
|
|
3
|
-
import { t as getUserRoles } from "./types-
|
|
4
|
-
import { t as MemoryCacheStore } from "./memory-
|
|
3
|
+
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
4
|
+
import { t as MemoryCacheStore } from "./memory-TdTHCS9-.mjs";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
-
//#region src/permissions/applyPermissionResult.ts
|
|
7
|
-
/**
|
|
8
|
-
* Normalize a permission check return value (`boolean | PermissionResult`)
|
|
9
|
-
* into a concrete `PermissionResult`. This is the only place in Arc that
|
|
10
|
-
* promotes booleans to results — keeps the type narrowing honest everywhere.
|
|
11
|
-
*/
|
|
12
|
-
function normalizePermissionResult(result) {
|
|
13
|
-
if (typeof result === "boolean") return { granted: result };
|
|
14
|
-
return result;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Apply a granted `PermissionResult` to a Fastify request — merges row-level
|
|
18
|
-
* filters into `_policyFilters` and conditionally installs the scope.
|
|
19
|
-
*
|
|
20
|
-
* **Scope install rule:** only writes `scope` when the current request scope
|
|
21
|
-
* is absent or `public`. This prevents downgrading an already-authenticated
|
|
22
|
-
* request (e.g. Better Auth set `member`, then a permission check returns a
|
|
23
|
-
* narrower `service` scope — the original `member` wins because it came from
|
|
24
|
-
* a more authoritative source).
|
|
25
|
-
*
|
|
26
|
-
* Safe to call with a non-granted result — it simply no-ops. Callers should
|
|
27
|
-
* still check `result.granted` and send an error response before reaching here,
|
|
28
|
-
* but this function tolerates the misuse defensively.
|
|
29
|
-
*/
|
|
30
|
-
function applyPermissionResult(result, request) {
|
|
31
|
-
if (!result.granted) return;
|
|
32
|
-
if (result.filters) request._policyFilters = {
|
|
33
|
-
...request._policyFilters ?? {},
|
|
34
|
-
...result.filters
|
|
35
|
-
};
|
|
36
|
-
if (result.scope) {
|
|
37
|
-
const current = request.scope;
|
|
38
|
-
if (!current || current.kind === "public") request.scope = result.scope;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Max length of a `PermissionResult.reason` string before we fall back to the
|
|
43
|
-
* generic default message. Upstream checks can return arbitrary strings; we
|
|
44
|
-
* clamp to prevent accidental leakage of internal diagnostics or oversized
|
|
45
|
-
* payloads via the 4xx response body.
|
|
46
|
-
*/
|
|
47
|
-
const MAX_DENIAL_REASON_LENGTH = 100;
|
|
48
|
-
/**
|
|
49
|
-
* End-to-end evaluator: runs the permission check, catches throws, normalizes
|
|
50
|
-
* the result, sends a 401/403 response on denial, and applies side-effects on
|
|
51
|
-
* grant. Returns `true` if the caller should continue, `false` if a response
|
|
52
|
-
* has been sent and the caller should return.
|
|
53
|
-
*
|
|
54
|
-
* This is the single source of truth for the 5-step sequence shared by the
|
|
55
|
-
* CRUD router, action router, and MCP tool handlers:
|
|
56
|
-
*
|
|
57
|
-
* 1. `try { await check(ctx) } catch { reply 403 }`
|
|
58
|
-
* 2. `normalizePermissionResult(result)`
|
|
59
|
-
* 3. If denied → 401 (no user) or 403 (user) with clamped reason
|
|
60
|
-
* 4. If granted → `applyPermissionResult` (filters + scope)
|
|
61
|
-
* 5. Return true/false so the caller knows whether to keep going
|
|
62
|
-
*
|
|
63
|
-
* Context construction, pre-check auth gating, and success-path handler
|
|
64
|
-
* invocation stay at the callsite — those are genuinely different per router
|
|
65
|
-
* and don't belong here.
|
|
66
|
-
*
|
|
67
|
-
* @returns `true` if authorized (caller continues), `false` if a response was sent
|
|
68
|
-
*/
|
|
69
|
-
async function evaluateAndApplyPermission(check, context, request, reply, opts) {
|
|
70
|
-
let result;
|
|
71
|
-
try {
|
|
72
|
-
result = await check(context);
|
|
73
|
-
} catch (err) {
|
|
74
|
-
request.log?.warn?.({
|
|
75
|
-
err,
|
|
76
|
-
resource: context.resource,
|
|
77
|
-
action: context.action
|
|
78
|
-
}, "Permission check threw");
|
|
79
|
-
reply.code(403).send({
|
|
80
|
-
code: "arc.forbidden",
|
|
81
|
-
message: "Permission denied",
|
|
82
|
-
status: 403
|
|
83
|
-
});
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
const permResult = normalizePermissionResult(result);
|
|
87
|
-
if (!permResult.granted) {
|
|
88
|
-
const defaultMsg = opts?.defaultDenialMessage?.(context.user) ?? (context.user ? "Permission denied" : "Authentication required");
|
|
89
|
-
const reason = permResult.reason && permResult.reason.length <= MAX_DENIAL_REASON_LENGTH ? permResult.reason : defaultMsg;
|
|
90
|
-
const status = context.user ? 403 : 401;
|
|
91
|
-
reply.code(status).send({
|
|
92
|
-
code: context.user ? "arc.forbidden" : "arc.unauthorized",
|
|
93
|
-
message: reason,
|
|
94
|
-
status
|
|
95
|
-
});
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
applyPermissionResult(permResult, request);
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
//#endregion
|
|
102
6
|
//#region src/permissions/fields.ts
|
|
103
7
|
/**
|
|
104
8
|
* Field-Level Permissions
|
|
@@ -266,6 +170,102 @@ function resolveEffectiveRoles(userRoles, orgRoles) {
|
|
|
266
170
|
return [...new Set([...userRoles, ...orgRoles])];
|
|
267
171
|
}
|
|
268
172
|
//#endregion
|
|
173
|
+
//#region src/permissions/applyPermissionResult.ts
|
|
174
|
+
/**
|
|
175
|
+
* Normalize a permission check return value (`boolean | PermissionResult`)
|
|
176
|
+
* into a concrete `PermissionResult`. This is the only place in Arc that
|
|
177
|
+
* promotes booleans to results — keeps the type narrowing honest everywhere.
|
|
178
|
+
*/
|
|
179
|
+
function normalizePermissionResult(result) {
|
|
180
|
+
if (typeof result === "boolean") return { granted: result };
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Apply a granted `PermissionResult` to a Fastify request — merges row-level
|
|
185
|
+
* filters into `_policyFilters` and conditionally installs the scope.
|
|
186
|
+
*
|
|
187
|
+
* **Scope install rule:** only writes `scope` when the current request scope
|
|
188
|
+
* is absent or `public`. This prevents downgrading an already-authenticated
|
|
189
|
+
* request (e.g. Better Auth set `member`, then a permission check returns a
|
|
190
|
+
* narrower `service` scope — the original `member` wins because it came from
|
|
191
|
+
* a more authoritative source).
|
|
192
|
+
*
|
|
193
|
+
* Safe to call with a non-granted result — it simply no-ops. Callers should
|
|
194
|
+
* still check `result.granted` and send an error response before reaching here,
|
|
195
|
+
* but this function tolerates the misuse defensively.
|
|
196
|
+
*/
|
|
197
|
+
function applyPermissionResult(result, request) {
|
|
198
|
+
if (!result.granted) return;
|
|
199
|
+
if (result.filters) request._policyFilters = {
|
|
200
|
+
...request._policyFilters ?? {},
|
|
201
|
+
...result.filters
|
|
202
|
+
};
|
|
203
|
+
if (result.scope) {
|
|
204
|
+
const current = request.scope;
|
|
205
|
+
if (!current || current.kind === "public") request.scope = result.scope;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Max length of a `PermissionResult.reason` string before we fall back to the
|
|
210
|
+
* generic default message. Upstream checks can return arbitrary strings; we
|
|
211
|
+
* clamp to prevent accidental leakage of internal diagnostics or oversized
|
|
212
|
+
* payloads via the 4xx response body.
|
|
213
|
+
*/
|
|
214
|
+
const MAX_DENIAL_REASON_LENGTH = 100;
|
|
215
|
+
/**
|
|
216
|
+
* End-to-end evaluator: runs the permission check, catches throws, normalizes
|
|
217
|
+
* the result, sends a 401/403 response on denial, and applies side-effects on
|
|
218
|
+
* grant. Returns `true` if the caller should continue, `false` if a response
|
|
219
|
+
* has been sent and the caller should return.
|
|
220
|
+
*
|
|
221
|
+
* This is the single source of truth for the 5-step sequence shared by the
|
|
222
|
+
* CRUD router, action router, and MCP tool handlers:
|
|
223
|
+
*
|
|
224
|
+
* 1. `try { await check(ctx) } catch { reply 403 }`
|
|
225
|
+
* 2. `normalizePermissionResult(result)`
|
|
226
|
+
* 3. If denied → 401 (no user) or 403 (user) with clamped reason
|
|
227
|
+
* 4. If granted → `applyPermissionResult` (filters + scope)
|
|
228
|
+
* 5. Return true/false so the caller knows whether to keep going
|
|
229
|
+
*
|
|
230
|
+
* Context construction, pre-check auth gating, and success-path handler
|
|
231
|
+
* invocation stay at the callsite — those are genuinely different per router
|
|
232
|
+
* and don't belong here.
|
|
233
|
+
*
|
|
234
|
+
* @returns `true` if authorized (caller continues), `false` if a response was sent
|
|
235
|
+
*/
|
|
236
|
+
async function evaluateAndApplyPermission(check, context, request, reply, opts) {
|
|
237
|
+
let result;
|
|
238
|
+
try {
|
|
239
|
+
result = await check(context);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
request.log?.warn?.({
|
|
242
|
+
err,
|
|
243
|
+
resource: context.resource,
|
|
244
|
+
action: context.action
|
|
245
|
+
}, "Permission check threw");
|
|
246
|
+
reply.code(403).send({
|
|
247
|
+
code: "arc.forbidden",
|
|
248
|
+
message: "Permission denied",
|
|
249
|
+
status: 403
|
|
250
|
+
});
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
const permResult = normalizePermissionResult(result);
|
|
254
|
+
if (!permResult.granted) {
|
|
255
|
+
const defaultMsg = opts?.defaultDenialMessage?.(context.user) ?? (context.user ? "Permission denied" : "Authentication required");
|
|
256
|
+
const reason = permResult.reason && permResult.reason.length <= MAX_DENIAL_REASON_LENGTH ? permResult.reason : defaultMsg;
|
|
257
|
+
const status = context.user ? 403 : 401;
|
|
258
|
+
reply.code(status).send({
|
|
259
|
+
code: context.user ? "arc.forbidden" : "arc.unauthorized",
|
|
260
|
+
message: reason,
|
|
261
|
+
status
|
|
262
|
+
});
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
applyPermissionResult(permResult, request);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
269
269
|
//#region src/permissions/agent.ts
|
|
270
270
|
/**
|
|
271
271
|
* Agent-Auth Permission Helpers — DPoP + capability mandates for AI-agent flows
|
|
@@ -1490,4 +1490,4 @@ function readOnly(overrides) {
|
|
|
1490
1490
|
}, overrides);
|
|
1491
1491
|
}
|
|
1492
1492
|
//#endregion
|
|
1493
|
-
export { requireMandate as A, requireAuth as C, when as D, roles as E,
|
|
1493
|
+
export { requireMandate as A, requireAuth as C, when as D, roles as E, applyFieldWritePermissions as F, fields as I, resolveEffectiveRoles as L, evaluateAndApplyPermission as M, normalizePermissionResult as N, requireAgentScope as O, applyFieldReadPermissions as P, not as S, requireRoles as T, requireTeamMembership as _, presets_exports as a, anyOf as b, readOnly as c, createOrgPermissions as d, requireOrgInScope as f, requireServiceScope as g, requireScopeContext as h, ownerWithAdminBypass as i, applyPermissionResult as j, requireDPoP as k, createRoleHierarchy as l, requireOrgRole as m, authenticated as n, publicRead as o, requireOrgMembership as p, fullPublic as r, publicReadAdminWrite as s, adminOnly as t, createDynamicPermissionMatrix as u, allOf as v, requireOwnership as w, denyAll as x, allowPublic as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ft as OperationFilter, It as PipelineConfig, Lt as PipelineContext, Mt as Guard, Nt as Interceptor, Pt as NextFunction, Rt as PipelineStep, yt as IControllerResponse, zt as Transform } from "../index-
|
|
1
|
+
import { Ft as OperationFilter, It as PipelineConfig, Lt as PipelineContext, Mt as Guard, Nt as Interceptor, Pt as NextFunction, Rt as PipelineStep, yt as IControllerResponse, zt as Transform } from "../index-QYma8mdC.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/pipeline/guard.d.ts
|
|
4
4
|
interface GuardOptions {
|
package/dist/pipeline/index.mjs
CHANGED