@classytic/arc 2.23.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-BQ8KNvWC.mjs → BaseController--M8EMzt0.mjs} +90 -21
- package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
- package/dist/actionPermissions-CAphcmUG.mjs +24 -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 +263 -259
- 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/docs.mjs +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.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-DQ6rDTyH.mjs → core-EUNu0u07.mjs} +296 -11
- package/dist/{createActionRouter-BuOv-m4Y.mjs → createActionRouter-B6GvAuAX.mjs} +3 -2
- package/dist/{createAggregationRouter-CgJv_BU_.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/{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 +163 -91
- 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 +4 -5
- package/dist/factory/index.mjs +28 -8
- package/dist/{fastify-augmentation-g_f9omSc.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.mjs +27 -3
- package/dist/{index-wMoWBLkn.d.mts → index-BMnbzvtO.d.mts} +3 -3
- package/dist/{integrations/jobs.d.mts → index-CG4yqMDB.d.mts} +82 -2
- package/dist/{index-BBsvQt7U.d.mts → index-DPRH0Cme.d.mts} +36 -106
- package/dist/{module-B4u10iOJ.d.mts → index-KUoxjIq1.d.mts} +263 -23
- package/dist/{index-CC5ruDGh.d.mts → index-f4PTrk3B.d.mts} +37 -5
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +5 -5
- package/dist/{init-CjO4p_q1.mjs → init-tlRSt667.mjs} +206 -208
- 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 +4 -4
- 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 +37 -30
- package/dist/integrations/webhooks.mjs +51 -62
- 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/{mcpPlugin-BtzIL_kM.mjs → mcpPlugin-Cb7olw0j.mjs} +11 -9
- 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-jSDx-VDv.mjs → openapi-DrLtog_B.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-tbxFHxjZ.mjs → permissions-BrtiBKGs.mjs} +229 -229
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{actionPermissions-lszoJle1.mjs → pipeline-BN5G05rd.mjs} +3 -26
- package/dist/plugins/index.d.mts +6 -77
- package/dist/plugins/index.mjs +11 -95
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{index-Ba10fdKp.d.mts → preloadResources-0oe-JSSS.d.mts} +50 -50
- package/dist/{createApp-CjOeWJ9_.mjs → preloadResources-DfyngIN9.mjs} +233 -70
- 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-BOe4Ugkl.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-CWO6mdYe.mjs → rateLimit-DrED-C4v.mjs} +2 -109
- package/dist/{realtime-I8OrfnMR.mjs → realtime-Cuw_deA-.mjs} +2 -2
- 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/renewingLease-3cuQH7h8.mjs +44 -0
- package/dist/{requestContext-BZKOr13H.mjs → requestContext-MrlJwECf.mjs} +2 -2
- package/dist/schedules-CjOd2MG9.mjs +113 -0
- 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-LONQ3mfZ.mjs → scope-DfaPntpo.mjs} +4 -5
- package/dist/{sse-0u4P4-OI.mjs → sse-BQ1w0dfl.mjs} +1 -1
- 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-5OuqeTDX.d.mts → types-QFGc3Cjv.d.mts} +1 -1
- 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 +58 -85
- package/dist/utils/index.mjs +4 -2
- package/dist/{utils-CZyeRXTh.mjs → utils-taUVD4p3.mjs} +47 -337
- package/dist/{validate-C2mGgSXg.mjs → validate-Bk44F2yT.mjs} +1 -1
- package/dist/{versioning-D6PvCwpq.d.mts → versioning-DXgY5BUm.d.mts} +11 -29
- 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 -513
- package/skills/arc/references/api-reference.md +3 -1
- package/skills/arc/references/integrations.md +20 -11
- 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/types-XTApa8b6.mjs +0 -24
|
@@ -1,233 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as MemoryCacheStore } from "./memory-
|
|
2
|
+
import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
|
|
3
|
+
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-CSyCxl2V.mjs";
|
|
4
|
+
import { t as MemoryCacheStore } from "./memory-CmnzXE5Z.mjs";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
-
//#region src/permissions/agent.ts
|
|
7
|
-
/**
|
|
8
|
-
* Agent-Auth Permission Helpers — DPoP + capability mandates for AI-agent flows
|
|
9
|
-
*
|
|
10
|
-
* Three checks for the 2025 agent-authorization stack (AP2 / Stripe x402 /
|
|
11
|
-
* MCP authorization / RFC 9700 / RFC 9449 / RFC 9728):
|
|
12
|
-
*
|
|
13
|
-
* - `requireDPoP()` — token must be sender-constrained (RFC 9449)
|
|
14
|
-
* - `requireMandate(cap, …)` — capability mandate must authorize this action
|
|
15
|
-
* - `requireAgentScope(opts)` — composite gate: service identity + mandate + DPoP
|
|
16
|
-
*
|
|
17
|
-
* Arc reads `request.scope.mandate` and `request.scope.dpopJkt` populated by
|
|
18
|
-
* your `authenticate` callback. Arc does **not** parse mandate JWTs/VCs or
|
|
19
|
-
* verify DPoP proofs — that's a 1-2 line `jose` call in your authenticator.
|
|
20
|
-
* Arc validates *what's already proved* against the action being attempted.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```typescript
|
|
24
|
-
* import { requireDPoP, requireMandate, requireAgentScope } from '@classytic/arc/permissions';
|
|
25
|
-
*
|
|
26
|
-
* defineResource({
|
|
27
|
-
* name: 'invoice',
|
|
28
|
-
* permissions: {
|
|
29
|
-
* pay: requireAgentScope({
|
|
30
|
-
* capability: 'payment.charge',
|
|
31
|
-
* requireDPoP: true,
|
|
32
|
-
* validateAmount: (ctx, mandate) =>
|
|
33
|
-
* typeof ctx.data?.amount === 'number' && ctx.data.amount <= (mandate.cap ?? 0),
|
|
34
|
-
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
35
|
-
* }),
|
|
36
|
-
* },
|
|
37
|
-
* });
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
/** Default grace window for mandate `expiresAt` — accommodates clock skew. */
|
|
41
|
-
const DEFAULT_TTL_GRACE_MS = 3e4;
|
|
42
|
-
/**
|
|
43
|
-
* Require a sender-constrained credential — the inbound token MUST carry a
|
|
44
|
-
* DPoP proof (RFC 9449) bound to a known key. Arc reads `scope.dpopJkt` (the
|
|
45
|
-
* JWK SHA-256 thumbprint per RFC 7638); your `authenticate` function performs
|
|
46
|
-
* the cryptographic `jose.dpop.verify(...)` and sets the field on success.
|
|
47
|
-
*
|
|
48
|
-
* **Pass behavior:**
|
|
49
|
-
* - `service` scope where `dpopJkt` is set → grant
|
|
50
|
-
* - `elevated` scope → grant (platform admin bypass)
|
|
51
|
-
* - Anything else → deny with a clear reason
|
|
52
|
-
*
|
|
53
|
-
* Use for high-value endpoints where bearer-token replay must be impossible:
|
|
54
|
-
* payment charges, data exports, account-takeover-class admin actions.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```typescript
|
|
58
|
-
* permissions: { charge: allOf(requireServiceScope('payment.write'), requireDPoP()) }
|
|
59
|
-
* ```
|
|
60
|
-
*/
|
|
61
|
-
function requireDPoP() {
|
|
62
|
-
const check = (ctx) => {
|
|
63
|
-
const scope = getRequestScope(ctx.request);
|
|
64
|
-
if (isElevated(scope)) return true;
|
|
65
|
-
if (!isService(scope)) return {
|
|
66
|
-
granted: false,
|
|
67
|
-
reason: "DPoP-bound service identity required. Configure your authenticate callback to set scope.dpopJkt after verifying the DPoP proof header (RFC 9449)."
|
|
68
|
-
};
|
|
69
|
-
if (!getDPoPJkt(scope)) return {
|
|
70
|
-
granted: false,
|
|
71
|
-
reason: "Sender-constrained credential required (DPoP). Inbound token is bearer; replay-resistance is mandatory on this endpoint."
|
|
72
|
-
};
|
|
73
|
-
return true;
|
|
74
|
-
};
|
|
75
|
-
check._dpopRequired = true;
|
|
76
|
-
return check;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Require a capability mandate (AP2 / x402 / MCP authorization) that
|
|
80
|
-
* authorizes the action being attempted.
|
|
81
|
-
*
|
|
82
|
-
* The mandate is set on `request.scope.mandate` by your authenticate function
|
|
83
|
-
* after verifying the inbound mandate JWT/VC. This check validates that the
|
|
84
|
-
* presented mandate covers the requested capability, hasn't expired, is bound
|
|
85
|
-
* to the right resource (when `audience` opt is set), and respects the
|
|
86
|
-
* mandate's numeric ceiling (when `validateAmount` opt is set).
|
|
87
|
-
*
|
|
88
|
-
* **Pass behavior:**
|
|
89
|
-
* - `elevated` scope → grant unless `noElevatedBypass: true`
|
|
90
|
-
* - `service` scope with mandate matching `capability`, not expired, and
|
|
91
|
-
* passing `validateAmount` + `audience` checks → grant
|
|
92
|
-
* - Anything else → deny with a precise reason
|
|
93
|
-
*
|
|
94
|
-
* Pair with `requireDPoP()` for replay-resistance, or use the bundled
|
|
95
|
-
* `requireAgentScope(...)` to declare both at once.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```typescript
|
|
99
|
-
* // Single payment charge — amount must fit the mandate's cap
|
|
100
|
-
* permissions: {
|
|
101
|
-
* pay: requireMandate('payment.charge', {
|
|
102
|
-
* validateAmount: (ctx, m) => (ctx.data as { amount: number }).amount <= (m.cap ?? 0),
|
|
103
|
-
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
104
|
-
* }),
|
|
105
|
-
* }
|
|
106
|
-
*
|
|
107
|
-
* // Boolean capability — presence of mandate is the gate
|
|
108
|
-
* permissions: {
|
|
109
|
-
* exportData: requireMandate('data.export'),
|
|
110
|
-
* }
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
function requireMandate(capability, opts = {}) {
|
|
114
|
-
if (!capability || typeof capability !== "string") throw new Error("requireMandate(capability) requires a non-empty capability string (e.g. 'payment.charge')");
|
|
115
|
-
const ttlGrace = opts.ttlGraceMs ?? DEFAULT_TTL_GRACE_MS;
|
|
116
|
-
const noElevatedBypass = opts.noElevatedBypass === true;
|
|
117
|
-
const check = (ctx) => {
|
|
118
|
-
const scope = getRequestScope(ctx.request);
|
|
119
|
-
if (isElevated(scope) && !noElevatedBypass) return true;
|
|
120
|
-
const mandate = getMandate(scope);
|
|
121
|
-
if (!mandate) return {
|
|
122
|
-
granted: false,
|
|
123
|
-
reason: `Capability mandate required (${capability}). Configure your authenticate callback to populate request.scope.mandate after verifying the mandate JWT/VC.`
|
|
124
|
-
};
|
|
125
|
-
if (mandate.capability !== capability) return {
|
|
126
|
-
granted: false,
|
|
127
|
-
reason: `Mandate authorizes "${mandate.capability}", not "${capability}"`
|
|
128
|
-
};
|
|
129
|
-
if (mandate.expiresAt !== void 0 && Date.now() > mandate.expiresAt + ttlGrace) return {
|
|
130
|
-
granted: false,
|
|
131
|
-
reason: `Mandate expired at ${new Date(mandate.expiresAt).toISOString()}`
|
|
132
|
-
};
|
|
133
|
-
if (opts.audience) {
|
|
134
|
-
const required = typeof opts.audience === "function" ? opts.audience(ctx) : opts.audience;
|
|
135
|
-
if (required && mandate.audience && mandate.audience !== required) return {
|
|
136
|
-
granted: false,
|
|
137
|
-
reason: `Mandate is bound to "${mandate.audience}", not "${required}"`
|
|
138
|
-
};
|
|
139
|
-
if (required && !mandate.audience) return {
|
|
140
|
-
granted: false,
|
|
141
|
-
reason: `Mandate must be bound to "${required}" (no audience claim)`
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
if (opts.validateAmount) {
|
|
145
|
-
const result = opts.validateAmount(ctx, mandate);
|
|
146
|
-
if (result !== true) return {
|
|
147
|
-
granted: false,
|
|
148
|
-
reason: typeof result === "string" ? result : `Action exceeds mandate cap ${mandate.cap}${mandate.currency ? ` ${mandate.currency}` : ""}`
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
return true;
|
|
152
|
-
};
|
|
153
|
-
check._mandateCapability = capability;
|
|
154
|
-
return check;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Composite gate for AI-agent / M2M flows on protected resources.
|
|
158
|
-
*
|
|
159
|
-
* Bundles the three things every high-value agent endpoint needs:
|
|
160
|
-
* 1. **Service identity** — `scope.kind === 'service'` with `clientId`
|
|
161
|
-
* 2. **Capability mandate** — narrows what *this request* may do
|
|
162
|
-
* 3. **DPoP binding** — credential cannot be replayed from a different key
|
|
163
|
-
*
|
|
164
|
-
* Use this instead of hand-composing `allOf(requireServiceScope(...),
|
|
165
|
-
* requireMandate(...), requireDPoP())` — fewer ways to misconfigure, one
|
|
166
|
-
* meta-tag downstream tools (audit, MCP, OpenAPI) can read.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* import { requireAgentScope } from '@classytic/arc/permissions';
|
|
171
|
-
*
|
|
172
|
-
* defineResource({
|
|
173
|
-
* name: 'invoice',
|
|
174
|
-
* actions: {
|
|
175
|
-
* pay: {
|
|
176
|
-
* handler: payInvoice,
|
|
177
|
-
* permissions: requireAgentScope({
|
|
178
|
-
* capability: 'payment.charge',
|
|
179
|
-
* scopes: ['payment.write'],
|
|
180
|
-
* requireDPoP: true,
|
|
181
|
-
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
182
|
-
* validateAmount: (ctx, m) => (ctx.data as { amount: number }).amount <= (m.cap ?? 0),
|
|
183
|
-
* }),
|
|
184
|
-
* },
|
|
185
|
-
* },
|
|
186
|
-
* });
|
|
187
|
-
* ```
|
|
188
|
-
*/
|
|
189
|
-
function requireAgentScope(opts) {
|
|
190
|
-
const { capability, scopes, requireDPoP: needsDPoP = true, ...mandateOpts } = opts;
|
|
191
|
-
if (!capability) throw new Error("requireAgentScope({ capability }) is required");
|
|
192
|
-
const mandateCheck = requireMandate(capability, mandateOpts);
|
|
193
|
-
const dpopCheck = needsDPoP ? requireDPoP() : null;
|
|
194
|
-
const requiredScopes = scopes && scopes.length > 0 ? [...scopes] : null;
|
|
195
|
-
const check = async (ctx) => {
|
|
196
|
-
const scope = getRequestScope(ctx.request);
|
|
197
|
-
if (isElevated(scope) && !mandateOpts.noElevatedBypass) return true;
|
|
198
|
-
if (!isService(scope)) return {
|
|
199
|
-
granted: false,
|
|
200
|
-
reason: "Service identity required (machine principal). Agent flows must authenticate as a service, not a logged-in user."
|
|
201
|
-
};
|
|
202
|
-
if (requiredScopes) {
|
|
203
|
-
const granted = scope.scopes ?? [];
|
|
204
|
-
if (!requiredScopes.some((s) => granted.includes(s))) return {
|
|
205
|
-
granted: false,
|
|
206
|
-
reason: `Service identity is missing required OAuth scope(s): ${requiredScopes.join(", ")}`
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const mandateResult = await mandateCheck(ctx);
|
|
210
|
-
if (mandateResult !== true) return typeof mandateResult === "object" ? mandateResult : {
|
|
211
|
-
granted: false,
|
|
212
|
-
reason: "Mandate check failed"
|
|
213
|
-
};
|
|
214
|
-
if (dpopCheck) {
|
|
215
|
-
const dpopResult = await dpopCheck(ctx);
|
|
216
|
-
if (dpopResult !== true) return typeof dpopResult === "object" ? dpopResult : {
|
|
217
|
-
granted: false,
|
|
218
|
-
reason: "DPoP binding required"
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return true;
|
|
222
|
-
};
|
|
223
|
-
check._agentScope = {
|
|
224
|
-
capability,
|
|
225
|
-
scopes: requiredScopes ?? void 0,
|
|
226
|
-
dpop: needsDPoP
|
|
227
|
-
};
|
|
228
|
-
return check;
|
|
229
|
-
}
|
|
230
|
-
//#endregion
|
|
231
6
|
//#region src/permissions/core.ts
|
|
232
7
|
/**
|
|
233
8
|
* Normalize a `string | [readonly string[]]` rest-args tuple into a single
|
|
@@ -517,6 +292,231 @@ function when(condition) {
|
|
|
517
292
|
};
|
|
518
293
|
}
|
|
519
294
|
//#endregion
|
|
295
|
+
//#region src/permissions/agent.ts
|
|
296
|
+
/**
|
|
297
|
+
* Agent-Auth Permission Helpers — DPoP + capability mandates for AI-agent flows
|
|
298
|
+
*
|
|
299
|
+
* Three checks for the 2025 agent-authorization stack (AP2 / Stripe x402 /
|
|
300
|
+
* MCP authorization / RFC 9700 / RFC 9449 / RFC 9728):
|
|
301
|
+
*
|
|
302
|
+
* - `requireDPoP()` — token must be sender-constrained (RFC 9449)
|
|
303
|
+
* - `requireMandate(cap, …)` — capability mandate must authorize this action
|
|
304
|
+
* - `requireAgentScope(opts)` — composite gate: service identity + mandate + DPoP
|
|
305
|
+
*
|
|
306
|
+
* Arc reads `request.scope.mandate` and `request.scope.dpopJkt` populated by
|
|
307
|
+
* your `authenticate` callback. Arc does **not** parse mandate JWTs/VCs or
|
|
308
|
+
* verify DPoP proofs — that's a 1-2 line `jose` call in your authenticator.
|
|
309
|
+
* Arc validates *what's already proved* against the action being attempted.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* import { requireDPoP, requireMandate, requireAgentScope } from '@classytic/arc/permissions';
|
|
314
|
+
*
|
|
315
|
+
* defineResource({
|
|
316
|
+
* name: 'invoice',
|
|
317
|
+
* permissions: {
|
|
318
|
+
* pay: requireAgentScope({
|
|
319
|
+
* capability: 'payment.charge',
|
|
320
|
+
* requireDPoP: true,
|
|
321
|
+
* validateAmount: (ctx, mandate) =>
|
|
322
|
+
* typeof ctx.data?.amount === 'number' && ctx.data.amount <= (mandate.cap ?? 0),
|
|
323
|
+
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
324
|
+
* }),
|
|
325
|
+
* },
|
|
326
|
+
* });
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
/** Default grace window for mandate `expiresAt` — accommodates clock skew. */
|
|
330
|
+
const DEFAULT_TTL_GRACE_MS = 3e4;
|
|
331
|
+
/**
|
|
332
|
+
* Require a sender-constrained credential — the inbound token MUST carry a
|
|
333
|
+
* DPoP proof (RFC 9449) bound to a known key. Arc reads `scope.dpopJkt` (the
|
|
334
|
+
* JWK SHA-256 thumbprint per RFC 7638); your `authenticate` function performs
|
|
335
|
+
* the cryptographic `jose.dpop.verify(...)` and sets the field on success.
|
|
336
|
+
*
|
|
337
|
+
* **Pass behavior:**
|
|
338
|
+
* - `service` scope where `dpopJkt` is set → grant
|
|
339
|
+
* - `elevated` scope → grant (platform admin bypass)
|
|
340
|
+
* - Anything else → deny with a clear reason
|
|
341
|
+
*
|
|
342
|
+
* Use for high-value endpoints where bearer-token replay must be impossible:
|
|
343
|
+
* payment charges, data exports, account-takeover-class admin actions.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* permissions: { charge: allOf(requireServiceScope('payment.write'), requireDPoP()) }
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
function requireDPoP() {
|
|
351
|
+
const check = (ctx) => {
|
|
352
|
+
const scope = getRequestScope(ctx.request);
|
|
353
|
+
if (isElevated(scope)) return true;
|
|
354
|
+
if (!isService(scope)) return {
|
|
355
|
+
granted: false,
|
|
356
|
+
reason: "DPoP-bound service identity required. Configure your authenticate callback to set scope.dpopJkt after verifying the DPoP proof header (RFC 9449)."
|
|
357
|
+
};
|
|
358
|
+
if (!getDPoPJkt(scope)) return {
|
|
359
|
+
granted: false,
|
|
360
|
+
reason: "Sender-constrained credential required (DPoP). Inbound token is bearer; replay-resistance is mandatory on this endpoint."
|
|
361
|
+
};
|
|
362
|
+
return true;
|
|
363
|
+
};
|
|
364
|
+
check._dpopRequired = true;
|
|
365
|
+
return check;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Require a capability mandate (AP2 / x402 / MCP authorization) that
|
|
369
|
+
* authorizes the action being attempted.
|
|
370
|
+
*
|
|
371
|
+
* The mandate is set on `request.scope.mandate` by your authenticate function
|
|
372
|
+
* after verifying the inbound mandate JWT/VC. This check validates that the
|
|
373
|
+
* presented mandate covers the requested capability, hasn't expired, is bound
|
|
374
|
+
* to the right resource (when `audience` opt is set), and respects the
|
|
375
|
+
* mandate's numeric ceiling (when `validateAmount` opt is set).
|
|
376
|
+
*
|
|
377
|
+
* **Pass behavior:**
|
|
378
|
+
* - `elevated` scope → grant unless `noElevatedBypass: true`
|
|
379
|
+
* - `service` scope with mandate matching `capability`, not expired, and
|
|
380
|
+
* passing `validateAmount` + `audience` checks → grant
|
|
381
|
+
* - Anything else → deny with a precise reason
|
|
382
|
+
*
|
|
383
|
+
* Pair with `requireDPoP()` for replay-resistance, or use the bundled
|
|
384
|
+
* `requireAgentScope(...)` to declare both at once.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```typescript
|
|
388
|
+
* // Single payment charge — amount must fit the mandate's cap
|
|
389
|
+
* permissions: {
|
|
390
|
+
* pay: requireMandate('payment.charge', {
|
|
391
|
+
* validateAmount: (ctx, m) => (ctx.data as { amount: number }).amount <= (m.cap ?? 0),
|
|
392
|
+
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
393
|
+
* }),
|
|
394
|
+
* }
|
|
395
|
+
*
|
|
396
|
+
* // Boolean capability — presence of mandate is the gate
|
|
397
|
+
* permissions: {
|
|
398
|
+
* exportData: requireMandate('data.export'),
|
|
399
|
+
* }
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
function requireMandate(capability, opts = {}) {
|
|
403
|
+
if (!capability || typeof capability !== "string") throw new Error("requireMandate(capability) requires a non-empty capability string (e.g. 'payment.charge')");
|
|
404
|
+
const ttlGrace = opts.ttlGraceMs ?? DEFAULT_TTL_GRACE_MS;
|
|
405
|
+
const noElevatedBypass = opts.noElevatedBypass === true;
|
|
406
|
+
const check = (ctx) => {
|
|
407
|
+
const scope = getRequestScope(ctx.request);
|
|
408
|
+
if (isElevated(scope) && !noElevatedBypass) return true;
|
|
409
|
+
const mandate = getMandate(scope);
|
|
410
|
+
if (!mandate) return {
|
|
411
|
+
granted: false,
|
|
412
|
+
reason: `Capability mandate required (${capability}). Configure your authenticate callback to populate request.scope.mandate after verifying the mandate JWT/VC.`
|
|
413
|
+
};
|
|
414
|
+
if (mandate.capability !== capability) return {
|
|
415
|
+
granted: false,
|
|
416
|
+
reason: `Mandate authorizes "${mandate.capability}", not "${capability}"`
|
|
417
|
+
};
|
|
418
|
+
if (mandate.expiresAt !== void 0 && Date.now() > mandate.expiresAt + ttlGrace) return {
|
|
419
|
+
granted: false,
|
|
420
|
+
reason: `Mandate expired at ${new Date(mandate.expiresAt).toISOString()}`
|
|
421
|
+
};
|
|
422
|
+
if (opts.audience) {
|
|
423
|
+
const required = typeof opts.audience === "function" ? opts.audience(ctx) : opts.audience;
|
|
424
|
+
if (required && mandate.audience && mandate.audience !== required) return {
|
|
425
|
+
granted: false,
|
|
426
|
+
reason: `Mandate is bound to "${mandate.audience}", not "${required}"`
|
|
427
|
+
};
|
|
428
|
+
if (required && !mandate.audience) return {
|
|
429
|
+
granted: false,
|
|
430
|
+
reason: `Mandate must be bound to "${required}" (no audience claim)`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (opts.validateAmount) {
|
|
434
|
+
const result = opts.validateAmount(ctx, mandate);
|
|
435
|
+
if (result !== true) return {
|
|
436
|
+
granted: false,
|
|
437
|
+
reason: typeof result === "string" ? result : `Action exceeds mandate cap ${mandate.cap}${mandate.currency ? ` ${mandate.currency}` : ""}`
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
return true;
|
|
441
|
+
};
|
|
442
|
+
check._mandateCapability = capability;
|
|
443
|
+
return check;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Composite gate for AI-agent / M2M flows on protected resources.
|
|
447
|
+
*
|
|
448
|
+
* Bundles the three things every high-value agent endpoint needs:
|
|
449
|
+
* 1. **Service identity** — `scope.kind === 'service'` with `clientId`
|
|
450
|
+
* 2. **Capability mandate** — narrows what *this request* may do
|
|
451
|
+
* 3. **DPoP binding** — credential cannot be replayed from a different key
|
|
452
|
+
*
|
|
453
|
+
* Use this instead of hand-composing `allOf(requireServiceScope(...),
|
|
454
|
+
* requireMandate(...), requireDPoP())` — fewer ways to misconfigure, one
|
|
455
|
+
* meta-tag downstream tools (audit, MCP, OpenAPI) can read.
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* import { requireAgentScope } from '@classytic/arc/permissions';
|
|
460
|
+
*
|
|
461
|
+
* defineResource({
|
|
462
|
+
* name: 'invoice',
|
|
463
|
+
* actions: {
|
|
464
|
+
* pay: {
|
|
465
|
+
* handler: payInvoice,
|
|
466
|
+
* permissions: requireAgentScope({
|
|
467
|
+
* capability: 'payment.charge',
|
|
468
|
+
* scopes: ['payment.write'],
|
|
469
|
+
* requireDPoP: true,
|
|
470
|
+
* audience: (ctx) => `invoice:${ctx.params?.id}`,
|
|
471
|
+
* validateAmount: (ctx, m) => (ctx.data as { amount: number }).amount <= (m.cap ?? 0),
|
|
472
|
+
* }),
|
|
473
|
+
* },
|
|
474
|
+
* },
|
|
475
|
+
* });
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
function requireAgentScope(opts) {
|
|
479
|
+
const { capability, scopes, requireDPoP: needsDPoP = true, ...mandateOpts } = opts;
|
|
480
|
+
if (!capability) throw new Error("requireAgentScope({ capability }) is required");
|
|
481
|
+
const mandateCheck = requireMandate(capability, mandateOpts);
|
|
482
|
+
const dpopCheck = needsDPoP ? requireDPoP() : null;
|
|
483
|
+
const requiredScopes = scopes && scopes.length > 0 ? [...scopes] : null;
|
|
484
|
+
const check = async (ctx) => {
|
|
485
|
+
const scope = getRequestScope(ctx.request);
|
|
486
|
+
if (isElevated(scope) && !mandateOpts.noElevatedBypass) return true;
|
|
487
|
+
if (!isService(scope)) return {
|
|
488
|
+
granted: false,
|
|
489
|
+
reason: "Service identity required (machine principal). Agent flows must authenticate as a service, not a logged-in user."
|
|
490
|
+
};
|
|
491
|
+
if (requiredScopes) {
|
|
492
|
+
const granted = scope.scopes ?? [];
|
|
493
|
+
if (!requiredScopes.some((s) => granted.includes(s))) return {
|
|
494
|
+
granted: false,
|
|
495
|
+
reason: `Service identity is missing required OAuth scope(s): ${requiredScopes.join(", ")}`
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const mandateResult = await mandateCheck(ctx);
|
|
499
|
+
if (mandateResult !== true) return typeof mandateResult === "object" ? mandateResult : {
|
|
500
|
+
granted: false,
|
|
501
|
+
reason: "Mandate check failed"
|
|
502
|
+
};
|
|
503
|
+
if (dpopCheck) {
|
|
504
|
+
const dpopResult = await dpopCheck(ctx);
|
|
505
|
+
if (dpopResult !== true) return typeof dpopResult === "object" ? dpopResult : {
|
|
506
|
+
granted: false,
|
|
507
|
+
reason: "DPoP binding required"
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return true;
|
|
511
|
+
};
|
|
512
|
+
check._agentScope = {
|
|
513
|
+
capability,
|
|
514
|
+
scopes: requiredScopes ?? void 0,
|
|
515
|
+
dpop: needsDPoP
|
|
516
|
+
};
|
|
517
|
+
return check;
|
|
518
|
+
}
|
|
519
|
+
//#endregion
|
|
520
520
|
//#region src/permissions/scope.ts
|
|
521
521
|
/**
|
|
522
522
|
* Permission Scope — checks bound to RequestScope.
|
|
@@ -1343,4 +1343,4 @@ function readOnly(overrides) {
|
|
|
1343
1343
|
}, overrides);
|
|
1344
1344
|
}
|
|
1345
1345
|
//#endregion
|
|
1346
|
-
export {
|
|
1346
|
+
export { requireAuth as A, requireDPoP as C, anyOf as D, allowPublic as E, requireRoles as M, roles as N, denyAll as O, when as P, requireAgentScope as S, allOf as T, requireOrgMembership as _, permissionMatrix as a, requireServiceScope as b, publicReadAdminWrite as c, GRANT_MODES as d, modeSatisfies as f, requireOrgInScope as g, createOrgPermissions as h, ownerWithAdminBypass as i, requireOwnership as j, not as k, readOnly as l, createDynamicPermissionMatrix as m, authenticated as n, presets_exports as o, requireGrant as p, fullPublic as r, publicRead as s, adminOnly as t, createRoleHierarchy as u, requireOrgRole as v, requireMandate as w, requireTeamMembership as x, requireScopeContext as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-
|
|
1
|
+
import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-DPRH0Cme.mjs";
|
|
2
2
|
//#region src/pipeline/guard.d.ts
|
|
3
3
|
interface GuardOptions {
|
|
4
4
|
operations?: OperationFilter;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
|
|
2
|
+
import { b as isElevated, c as getOrgId, g as getUserId, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
|
|
2
3
|
import { i as applyFieldReadPermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
|
|
3
|
-
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
4
4
|
import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
|
|
5
5
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
6
6
|
//#region src/scope/projection.ts
|
|
@@ -334,27 +334,4 @@ function buildActionPipelineHandler(handler, steps, operation, resourceName) {
|
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
336
|
//#endregion
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Return the effective `PermissionCheck` for a single action, or `undefined`
|
|
340
|
-
* when the resource declares no gate at any level.
|
|
341
|
-
*
|
|
342
|
-
* Callers decide what "no gate" means:
|
|
343
|
-
* - HTTP: boot-time throw in `normalizeActionsToRouterConfig`.
|
|
344
|
-
* - MCP: tool-generation throw in `resourceToTools` (mirrors HTTP — the
|
|
345
|
-
* two surfaces fail closed identically so MCP can't expose an
|
|
346
|
-
* unauthenticated mutating tool when the HTTP plugin lifecycle hasn't
|
|
347
|
-
* run).
|
|
348
|
-
* - OpenAPI: docs advertise the endpoint as unauthenticated.
|
|
349
|
-
*/
|
|
350
|
-
function resolveActionPermission(input) {
|
|
351
|
-
const { action, resourcePermissions, resourceActionPermissions, globalAuth } = input;
|
|
352
|
-
const explicit = typeof action !== "function" && action.permissions ? action.permissions : void 0;
|
|
353
|
-
if (explicit) return explicit;
|
|
354
|
-
if (resourceActionPermissions) return resourceActionPermissions;
|
|
355
|
-
if (globalAuth) return globalAuth;
|
|
356
|
-
const updateFallback = resourcePermissions?.update;
|
|
357
|
-
if (updateFallback) return updateFallback;
|
|
358
|
-
}
|
|
359
|
-
//#endregion
|
|
360
|
-
export { createCrudHandlers as a, getControllerContext as c, buildRequestScopeProjection as d, resolvePipelineSteps as i, getControllerScope as l, buildActionPipelineHandler as n, createFastifyHandler as o, buildPipelineHandler as r, createRequestContext as s, resolveActionPermission as t, sendControllerResponse as u };
|
|
337
|
+
export { createFastifyHandler as a, getControllerScope as c, createCrudHandlers as i, sendControllerResponse as l, buildPipelineHandler as n, createRequestContext as o, resolvePipelineSteps as r, getControllerContext as s, buildActionPipelineHandler as t, buildRequestScopeProjection as u };
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { At as RouteDefinition, J as PresetHook,
|
|
1
|
+
import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { c as AnyRecord } from "../types-Dmw-6Qeo.mjs";
|
|
2
3
|
import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-C6yv4ZTW.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { C as schedulesPlugin, D as healthPlugin, E as _default$3, S as _default$7, T as HealthOptions, b as ScheduleStats, w as HealthCheck, x as SchedulesPluginOptions, y as ScheduleDefinition } from "../index-KUoxjIq1.mjs";
|
|
5
|
+
import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-dPNBVeIj.mjs";
|
|
6
|
+
import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$5, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$8, n as _default$9, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-DXgY5BUm.mjs";
|
|
5
7
|
import { t as TracingOptions } from "../tracing-CsGKQJGg.mjs";
|
|
6
8
|
import { PaginatedResult } from "@classytic/repo-core/pagination";
|
|
7
9
|
import { FastifyInstance, FastifyPluginAsync } from "fastify";
|
|
@@ -165,77 +167,4 @@ declare function createRequestIdGenerator(opts?: Pick<RequestIdOptions, "header"
|
|
|
165
167
|
declare const requestIdPlugin: FastifyPluginAsync<RequestIdOptions>;
|
|
166
168
|
declare const _default$6: FastifyPluginAsync<RequestIdOptions>;
|
|
167
169
|
//#endregion
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Structural view of the ecosystem lock contract — matches
|
|
171
|
-
* `LockAdapter` from `@classytic/repo-core/lock` (and every kit
|
|
172
|
-
* implementation) WITHOUT importing the subpath, so arc's emitted
|
|
173
|
-
* declarations carry no repo-core@>=0.8 subpath reference and the
|
|
174
|
-
* peer floor stays honest. Any conforming adapter assigns.
|
|
175
|
-
*/
|
|
176
|
-
interface ScheduleLockLike {
|
|
177
|
-
tryAcquire(name: string, holderId: string, leaseMs: number): Promise<boolean> | boolean;
|
|
178
|
-
release(name: string, holderId: string): Promise<boolean> | boolean;
|
|
179
|
-
}
|
|
180
|
-
interface ScheduleDefinition {
|
|
181
|
-
/** Unique name — also the lock key (`arc:schedule:<name>`). */
|
|
182
|
-
name: string;
|
|
183
|
-
/** Interval in milliseconds between the END of one run and the next tick. */
|
|
184
|
-
every: number;
|
|
185
|
-
/** The work. Receives the Fastify instance; errors are logged, never fatal. */
|
|
186
|
-
handler: (fastify: FastifyInstance) => void | Promise<void>;
|
|
187
|
-
/** Fire once immediately when the app becomes ready (default: false). */
|
|
188
|
-
runOnStart?: boolean;
|
|
189
|
-
/**
|
|
190
|
-
* Random extra delay in `[0, jitterMs)` added to the FIRST tick only.
|
|
191
|
-
* Spreads boot-aligned jobs so multiple same-cadence schedules don't all
|
|
192
|
-
* fire at exactly `boot + every` after every restart (and, with a lock,
|
|
193
|
-
* de-synchronizes replicas competing for the same lease). Ignored when
|
|
194
|
-
* `runOnStart` is true.
|
|
195
|
-
*/
|
|
196
|
-
jitterMs?: number;
|
|
197
|
-
/**
|
|
198
|
-
* Lease duration when a `lock` is configured. Default: `every * 0.9`
|
|
199
|
-
* (capped at 5 minutes) — long enough that other replicas skip the same
|
|
200
|
-
* tick, short enough that a crashed leader fails over within one cycle.
|
|
201
|
-
*/
|
|
202
|
-
leaseMs?: number;
|
|
203
|
-
/** Per-schedule kill switch (default: true). */
|
|
204
|
-
enabled?: boolean;
|
|
205
|
-
}
|
|
206
|
-
interface SchedulesPluginOptions {
|
|
207
|
-
schedules: ScheduleDefinition[];
|
|
208
|
-
/**
|
|
209
|
-
* Ecosystem lock adapter for multi-replica deployments. Omit for
|
|
210
|
-
* single-instance apps — every tick runs locally.
|
|
211
|
-
*/
|
|
212
|
-
lock?: ScheduleLockLike;
|
|
213
|
-
/**
|
|
214
|
-
* Identity of this replica for lease ownership. Default: a random id per
|
|
215
|
-
* process (repo-core's `getInstanceId()` is a good explicit value).
|
|
216
|
-
*/
|
|
217
|
-
holderId?: string;
|
|
218
|
-
/**
|
|
219
|
-
* Master switch — `false` registers a typed no-op (config-gated
|
|
220
|
-
* environments), same pattern as `idempotencyPlugin`.
|
|
221
|
-
*/
|
|
222
|
-
enabled?: boolean;
|
|
223
|
-
}
|
|
224
|
-
/** Per-schedule runtime counters, readable via `fastify.getScheduleStats()`. */
|
|
225
|
-
interface ScheduleStats {
|
|
226
|
-
name: string;
|
|
227
|
-
runs: number;
|
|
228
|
-
failures: number;
|
|
229
|
-
skippedByLock: number;
|
|
230
|
-
lastRunAt: string | null;
|
|
231
|
-
lastError: string | null;
|
|
232
|
-
}
|
|
233
|
-
declare module "fastify" {
|
|
234
|
-
interface FastifyInstance {
|
|
235
|
-
getScheduleStats?: () => ScheduleStats[];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
declare const schedulesPlugin: FastifyPluginAsync<SchedulesPluginOptions>;
|
|
239
|
-
declare const _default$7: FastifyPluginAsync<SchedulesPluginOptions>;
|
|
240
|
-
//#endregion
|
|
241
|
-
export { type ArcCore, type ArcCorePluginOptions, type ArcPlugin, type CachingOptions, type CachingRule, type ChangeOperation, type CreatePluginDefinition, type ErrorHandlerOptions, type ErrorMapper, type GracefulShutdownOptions, type HealthCheck, type HealthOptions, type MetricEntry, type MetricsCollector, type MetricsOptions, type PluginMeta, type PluginResourceResult, type RealtimeOptions, type RequestIdOptions, type SSEOptions, type ScheduleDefinition, type ScheduleLockLike, type ScheduleStats, type SchedulesPluginOptions, type TracingOptions, type VersioningOptions, _default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, _default$1 as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, createRequestIdGenerator, defaultIsDuplicateKeyError, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, _default$2 as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, _default$3 as healthPlugin, healthPlugin as healthPluginFn, _default$4 as metricsPlugin, metricsPlugin as metricsPluginFn, _default$5 as realtimePlugin, realtimePlugin as realtimePluginFn, replyHelpersPlugin, _default$6 as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sanitizeRequestId, _default$7 as schedulesPlugin, schedulesPlugin as schedulesPluginFn, _default$8 as ssePlugin, ssePlugin as ssePluginFn, _default$9 as versioningPlugin, versioningPlugin as versioningPluginFn };
|
|
170
|
+
export { type ArcCore, type ArcCorePluginOptions, type ArcPlugin, type CachingOptions, type CachingRule, type ChangeOperation, type CreatePluginDefinition, type ErrorHandlerOptions, type ErrorMapper, type GracefulShutdownOptions, type HealthCheck, type HealthOptions, type MetricEntry, type MetricsCollector, type MetricsOptions, type PluginMeta, type PluginResourceResult, type RealtimeOptions, type RequestIdOptions, type SSEOptions, type ScheduleDefinition, type ScheduleStats, type SchedulesPluginOptions, type TracingOptions, type VersioningOptions, _default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, _default$1 as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, createRequestIdGenerator, defaultIsDuplicateKeyError, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, _default$2 as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, _default$3 as healthPlugin, healthPlugin as healthPluginFn, _default$4 as metricsPlugin, metricsPlugin as metricsPluginFn, _default$5 as realtimePlugin, realtimePlugin as realtimePluginFn, replyHelpersPlugin, _default$6 as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sanitizeRequestId, _default$7 as schedulesPlugin, schedulesPlugin as schedulesPluginFn, _default$8 as ssePlugin, ssePlugin as ssePluginFn, _default$9 as versioningPlugin, versioningPlugin as versioningPluginFn };
|