@classytic/arc 2.16.1 → 2.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/{BaseController-Vk8I7C0x.mjs → BaseController-CgHomZ4d.mjs} +5 -47
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/redis-session.mjs +28 -2
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-wKeGXn38.mjs → core-D8tpQ4ZL.mjs} +174 -31
- package/dist/{createActionRouter-eiOFtnC3.mjs → createActionRouter-BHUMb1-y.mjs} +5 -2
- package/dist/{createAggregationRouter-C4unhBnV.mjs → createAggregationRouter-BYVvhfc5.mjs} +1 -1
- package/dist/{createApp-DdPBtmzp.mjs → createApp-CzCEEsEm.mjs} +2 -2
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-Dci0AYLT.mjs → elevation-6mfTLBC-.mjs} +1 -1
- package/dist/{errorHandler-DNPzqaTT.mjs → errorHandler-wQejMBT3.mjs} +45 -4
- package/dist/events/transports/redis-stream-entry.mjs +34 -1
- package/dist/events/transports/redis.mjs +17 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +1 -1
- package/dist/{fastify-augmentation-CJm0vh-l.d.mts → fastify-augmentation-7LIR0nDv.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +20 -4
- package/dist/{index-DBDgy6Hw.d.mts → index-C1JSBan-.d.mts} +177 -1
- package/dist/{index-CC_pgZjY.d.mts → index-CruZir1o.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +5 -5
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/jobs.d.mts +36 -1
- package/dist/integrations/jobs.mjs +82 -9
- package/dist/integrations/mcp/index.d.mts +65 -3
- package/dist/integrations/mcp/index.mjs +93 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/websocket.mjs +2 -1
- package/dist/{mcpPlugin-Dbgz9_Rj.mjs → mcpPlugin-C6FViZXw.mjs} +111 -8
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{multipartBody-BOvVSVCD.mjs → multipartBody-VjVWmZl8.mjs} +2 -1
- package/dist/{openapi-BRnd3pT7.mjs → openapi-DKDs9NfD.mjs} +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-CTxMrreC.mjs → permissions-B1Qy55fg.mjs} +169 -169
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +3 -3
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +1 -1
- package/dist/presets/filesUpload.mjs +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +1 -1
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-C9BE6WaZ.mjs → presets-BYbPobbF.mjs} +5 -3
- package/dist/registry/index.d.mts +1 -1
- package/dist/{routerShared-ClV4cCaa.mjs → routerShared-nCyJGOKX.mjs} +127 -4
- package/dist/scope/index.mjs +2 -2
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +1 -1
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-Dw--SyTq.d.mts → types-BRmS8TNa.d.mts} +1 -1
- package/dist/{types-BD0BCdf6.d.mts → types-CoMjNmkF.d.mts} +12 -1
- package/dist/utils/index.d.mts +79 -2
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-DPXZnZUx.mjs → utils-BMQK8mRl.mjs} +111 -2
- package/package.json +1 -1
- package/skills/arc/SKILL.md +43 -1
- package/skills/arc/references/api-reference.md +2 -0
- package/skills/arc/references/mcp.md +30 -1
- /package/dist/{types-D57iXYb8.mjs → types-DV9WDfeg.mjs} +0 -0
|
@@ -337,7 +337,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
337
337
|
*/
|
|
338
338
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
339
339
|
if (!config.elevation) return;
|
|
340
|
-
const { elevationPlugin } = await import("./elevation-
|
|
340
|
+
const { elevationPlugin } = await import("./elevation-6mfTLBC-.mjs").then((n) => n.r);
|
|
341
341
|
await fastify.register(elevationPlugin, config.elevation);
|
|
342
342
|
trackPlugin("arc-elevation", config.elevation);
|
|
343
343
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -347,7 +347,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
347
347
|
*/
|
|
348
348
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
349
349
|
if (config.errorHandler === false) return;
|
|
350
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
350
|
+
const { errorHandlerPlugin } = await import("./errorHandler-wQejMBT3.mjs").then((n) => n.r);
|
|
351
351
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
352
352
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
353
353
|
trackPlugin("arc-error-handler", errorOpts);
|
package/dist/docs/index.d.mts
CHANGED
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getUserRoles } from "../types-
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
1
|
+
import { t as getUserRoles } from "../types-DV9WDfeg.mjs";
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DKDs9NfD.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { t as getUserRoles } from "./types-
|
|
3
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -39,13 +39,48 @@ function fastifyValidationDetails(error) {
|
|
|
39
39
|
return (error.validation ?? []).map((v) => {
|
|
40
40
|
const missingProperty = v.params?.missingProperty;
|
|
41
41
|
const path = v.instancePath?.replace(/^\//, "") || (typeof missingProperty === "string" ? missingProperty : void 0);
|
|
42
|
+
const meta = {};
|
|
43
|
+
if (v.keyword === "maximum" || v.keyword === "minimum" || v.keyword === "exclusiveMaximum" || v.keyword === "exclusiveMinimum") {
|
|
44
|
+
const bound = v.params?.limit;
|
|
45
|
+
if (typeof bound === "number") meta.bound = bound;
|
|
46
|
+
}
|
|
47
|
+
if (v.keyword === "enum") {
|
|
48
|
+
const allowed = v.params?.allowedValues;
|
|
49
|
+
if (Array.isArray(allowed)) meta.allowedValues = allowed;
|
|
50
|
+
}
|
|
42
51
|
return {
|
|
43
52
|
...path ? { path } : {},
|
|
44
53
|
code: v.keyword ?? "invalid",
|
|
45
|
-
message: v.message || "Invalid value"
|
|
54
|
+
message: v.message || "Invalid value",
|
|
55
|
+
...Object.keys(meta).length > 0 ? { meta } : {}
|
|
46
56
|
};
|
|
47
57
|
});
|
|
48
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Hoist a context-aware message + machine-readable `meta` for the
|
|
61
|
+
* common pagination-cap miss (`?limit=200` against a maxLimit=100
|
|
62
|
+
* resource). Pre-2.17.0 the wire envelope carried "Validation failed"
|
|
63
|
+
* + a detail with the AJV string; hosts had to grep the detail's
|
|
64
|
+
* message to surface the cap. Now the top-level message is
|
|
65
|
+
* "limit must be <= 100 (got 200)" AND `meta.cap` / `meta.received`
|
|
66
|
+
* are stamped on the contract for programmatic callers.
|
|
67
|
+
*/
|
|
68
|
+
function describePaginationCap(error, details) {
|
|
69
|
+
if (!Array.isArray(error.validation) || error.validation.length === 0) return null;
|
|
70
|
+
const v = error.validation.find((entry) => (entry.instancePath === "/limit" || entry.instancePath === "/page") && (entry.keyword === "maximum" || entry.keyword === "minimum"));
|
|
71
|
+
if (!v) return null;
|
|
72
|
+
const field = v.instancePath === "/limit" ? "limit" : "page";
|
|
73
|
+
const bound = v.params?.limit;
|
|
74
|
+
if (typeof bound !== "number") return null;
|
|
75
|
+
const detail = details.find((d) => d.path === field);
|
|
76
|
+
return {
|
|
77
|
+
message: detail?.message ? `Query parameter '${field}' ${detail.message} (cap is ${bound})` : `Query parameter '${field}' violates bound ${bound}`,
|
|
78
|
+
meta: {
|
|
79
|
+
field,
|
|
80
|
+
cap: bound
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
49
84
|
/** Map Mongoose `ValidationError.errors` → canonical `ErrorDetail[]`. */
|
|
50
85
|
function mongooseValidationDetails(errors) {
|
|
51
86
|
return Object.entries(errors).map(([field, e]) => ({
|
|
@@ -116,13 +151,19 @@ function classify(error, ctx) {
|
|
|
116
151
|
const fastifyErr = error;
|
|
117
152
|
if (Array.isArray(fastifyErr.validation)) {
|
|
118
153
|
const arcInternalCode = typeof fastifyErr.code === "string" && fastifyErr.code.startsWith("arc.") ? fastifyErr.code : void 0;
|
|
119
|
-
const
|
|
154
|
+
const details = fastifyValidationDetails(fastifyErr);
|
|
155
|
+
const paginationCap = arcInternalCode ? null : describePaginationCap(fastifyErr, details);
|
|
156
|
+
const message = paginationCap?.message ?? (arcInternalCode ? error.message || "Validation failed" : "Validation failed");
|
|
157
|
+
const mergedMeta = {
|
|
158
|
+
...fastifyErr.meta ?? {},
|
|
159
|
+
...paginationCap?.meta ?? {}
|
|
160
|
+
};
|
|
120
161
|
return {
|
|
121
162
|
code: arcInternalCode ?? "arc.validation_error",
|
|
122
163
|
message,
|
|
123
164
|
status: typeof fastifyErr.statusCode === "number" ? fastifyErr.statusCode : 400,
|
|
124
|
-
details
|
|
125
|
-
...
|
|
165
|
+
details,
|
|
166
|
+
...Object.keys(mergedMeta).length > 0 ? { meta: mergedMeta } : {}
|
|
126
167
|
};
|
|
127
168
|
}
|
|
128
169
|
if (typeof fastifyErr.statusCode === "number") return {
|
|
@@ -1,4 +1,37 @@
|
|
|
1
|
+
import sjson from "secure-json-parse";
|
|
1
2
|
//#region src/events/transports/redis-stream.ts
|
|
3
|
+
/**
|
|
4
|
+
* Redis Stream Event Transport — Durable Event Delivery
|
|
5
|
+
*
|
|
6
|
+
* Uses Redis Streams (`XADD`/`XREADGROUP`) for persistent, at-least-once event
|
|
7
|
+
* delivery across multiple service instances. Unlike Pub/Sub, events are stored
|
|
8
|
+
* in Redis and survive crashes/restarts.
|
|
9
|
+
*
|
|
10
|
+
* **Delivery guarantee:** at-least-once. Failed messages are left unacked and
|
|
11
|
+
* reclaimed after `claimTimeoutMs`, which can result in duplicate handler
|
|
12
|
+
* execution. Consumers must be idempotent (e.g. use `event.meta.id` as a
|
|
13
|
+
* deduplication key) to achieve effectively-once processing.
|
|
14
|
+
*
|
|
15
|
+
* Key features:
|
|
16
|
+
* - Consumer groups: each event delivered to exactly one consumer per group
|
|
17
|
+
* - Crash recovery: pending entries are auto-claimed after `claimTimeoutMs`
|
|
18
|
+
* - Dead letter stream: events exceeding `maxRetries` are moved to a DLQ
|
|
19
|
+
* - Backpressure: configurable block time and batch size
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { RedisStreamTransport } from '@classytic/arc/events';
|
|
24
|
+
* import Redis from 'ioredis';
|
|
25
|
+
*
|
|
26
|
+
* const transport = new RedisStreamTransport(new Redis(), {
|
|
27
|
+
* stream: 'arc-events',
|
|
28
|
+
* group: 'api-service',
|
|
29
|
+
* consumer: 'worker-1',
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* await app.register(eventPlugin, { transport });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
2
35
|
var RedisStreamTransport = class {
|
|
3
36
|
name = "redis-stream";
|
|
4
37
|
redis;
|
|
@@ -350,7 +383,7 @@ function parseStreamFields(fields) {
|
|
|
350
383
|
}
|
|
351
384
|
if (!eventType || !rawData) return null;
|
|
352
385
|
try {
|
|
353
|
-
const parsed =
|
|
386
|
+
const parsed = sjson.parse(rawData, (key, value) => {
|
|
354
387
|
if (key === "timestamp" && typeof value === "string") return new Date(value);
|
|
355
388
|
return value;
|
|
356
389
|
});
|
|
@@ -1,5 +1,21 @@
|
|
|
1
|
+
import sjson from "secure-json-parse";
|
|
1
2
|
//#region src/events/transports/redis.ts
|
|
2
3
|
/**
|
|
4
|
+
* Redis Event Transport
|
|
5
|
+
*
|
|
6
|
+
* Uses Redis Pub/Sub for cross-process event delivery.
|
|
7
|
+
* Optional dependency — only imported when explicitly used.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { RedisEventTransport } from '@classytic/arc/events/redis';
|
|
11
|
+
* import Redis from 'ioredis';
|
|
12
|
+
*
|
|
13
|
+
* const redis = new Redis();
|
|
14
|
+
* const transport = new RedisEventTransport(redis, { channel: 'arc-events' });
|
|
15
|
+
*
|
|
16
|
+
* await app.register(eventPlugin, { transport });
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
3
19
|
* DomainEvent contains a `Date` in `meta.timestamp`. We serialize it as an
|
|
4
20
|
* ISO string and revive it on the other end so subscribers always receive a
|
|
5
21
|
* proper Date object.
|
|
@@ -11,7 +27,7 @@ function serialize(event) {
|
|
|
11
27
|
});
|
|
12
28
|
}
|
|
13
29
|
function deserialize(raw) {
|
|
14
|
-
return
|
|
30
|
+
return sjson.parse(raw, (key, value) => {
|
|
15
31
|
if (key === "timestamp" && typeof value === "string") return new Date(value);
|
|
16
32
|
return value;
|
|
17
33
|
});
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, f as LoadResourcesOptions, h as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, m as ResourceModule, n as BetterAuthOption, o as JwtAuthOption, p as ResourceLike, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-
|
|
1
|
+
import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, f as LoadResourcesOptions, h as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, m as ResourceModule, n as BetterAuthOption, o as JwtAuthOption, p as ResourceLike, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-BRmS8TNa.mjs";
|
|
2
2
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/factory/createApp.d.ts
|
package/dist/factory/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-BOXzPhKU.mjs";
|
|
2
|
-
import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-
|
|
2
|
+
import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-CzCEEsEm.mjs";
|
|
3
3
|
import { t as loadResources } from "../loadResources-fol6JuvW.mjs";
|
|
4
4
|
//#region src/factory/edge.ts
|
|
5
5
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { An as HookSystem, z as ResourceRegistry } from "./index-
|
|
1
|
+
import { An as HookSystem, z as ResourceRegistry } from "./index-C1JSBan-.mjs";
|
|
2
2
|
import { t as ExternalOpenApiPaths } from "./externalPaths-ClykOQqa.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { An as HookSystem, Dn as HookOperation, En as HookHandler, Fn as beforeCreate, In as beforeDelete, Ln as beforeUpdate, Mn as afterCreate, Nn as afterDelete, On as HookPhase, Pn as afterUpdate, Rn as createHookSystem, Tn as HookContext, jn as HookSystemOptions, kn as HookRegistration, wn as DefineHookOptions, zn as defineHook } from "../index-
|
|
1
|
+
import { An as HookSystem, Dn as HookOperation, En as HookHandler, Fn as beforeCreate, In as beforeDelete, Ln as beforeUpdate, Mn as afterCreate, Nn as afterDelete, On as HookPhase, Pn as afterUpdate, Rn as createHookSystem, Tn as HookContext, jn as HookSystemOptions, kn as HookRegistration, wn as DefineHookOptions, zn as defineHook } from "../index-C1JSBan-.mjs";
|
|
2
2
|
export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
|
|
@@ -266,6 +266,12 @@ var MemoryIdempotencyStore = class {
|
|
|
266
266
|
*/
|
|
267
267
|
const HEADER_IDEMPOTENCY_REPLAYED = "x-idempotency-replayed";
|
|
268
268
|
const HEADER_IDEMPOTENCY_KEY = "x-idempotency-key";
|
|
269
|
+
/**
|
|
270
|
+
* Cap recursion depth in body fingerprinting. Real APIs almost never exceed
|
|
271
|
+
* ~10 levels; the cap is generous so well-formed payloads are unaffected,
|
|
272
|
+
* yet low enough to stop a `{"a":{"a":{...thousands deep...}}}` stack bomb.
|
|
273
|
+
*/
|
|
274
|
+
const MAX_FINGERPRINT_DEPTH = 32;
|
|
269
275
|
const idempotencyPlugin = async (fastify, opts = {}) => {
|
|
270
276
|
const { enabled = false, headerName = "idempotency-key", ttlMs = 864e5, lockTimeoutMs = 3e4, methods = [
|
|
271
277
|
"POST",
|
|
@@ -298,14 +304,24 @@ const idempotencyPlugin = async (fastify, opts = {}) => {
|
|
|
298
304
|
return true;
|
|
299
305
|
}
|
|
300
306
|
/**
|
|
301
|
-
* Normalize body for consistent hashing (sort keys recursively)
|
|
307
|
+
* Normalize body for consistent hashing (sort keys recursively).
|
|
308
|
+
*
|
|
309
|
+
* Recursion is bounded by `MAX_FINGERPRINT_DEPTH` to defeat a deep-nesting
|
|
310
|
+
* DoS: an attacker who sends a 1 MB JSON body of nested `{"a":{"a":...}}`
|
|
311
|
+
* would otherwise blow the call stack inside this hot path. The cap
|
|
312
|
+
* exceeds any sensible API shape (real bodies are <10 levels deep) while
|
|
313
|
+
* still being well under V8's default stack budget. At the limit we
|
|
314
|
+
* substitute a sentinel string so the hash still differs from siblings
|
|
315
|
+
* but recursion stops — the request is fingerprinted as if everything
|
|
316
|
+
* beyond the cap were `"<truncated>"`.
|
|
302
317
|
*/
|
|
303
|
-
function normalizeBody(obj) {
|
|
318
|
+
function normalizeBody(obj, depth = 0) {
|
|
304
319
|
if (obj === null || typeof obj !== "object") return obj;
|
|
305
|
-
if (
|
|
320
|
+
if (depth >= MAX_FINGERPRINT_DEPTH) return "<truncated>";
|
|
321
|
+
if (Array.isArray(obj)) return obj.map((item) => normalizeBody(item, depth + 1));
|
|
306
322
|
const sorted = {};
|
|
307
323
|
const keys = Object.keys(obj).sort();
|
|
308
|
-
for (const key of keys) sorted[key] = normalizeBody(obj[key]);
|
|
324
|
+
for (const key of keys) sorted[key] = normalizeBody(obj[key], depth + 1);
|
|
309
325
|
return sorted;
|
|
310
326
|
}
|
|
311
327
|
/**
|
|
@@ -2458,8 +2458,42 @@ interface RouteDefinition {
|
|
|
2458
2458
|
* @default false
|
|
2459
2459
|
*/
|
|
2460
2460
|
readonly tenantScope?: boolean;
|
|
2461
|
-
/**
|
|
2461
|
+
/**
|
|
2462
|
+
* SSE streaming mode. Two handler shapes work:
|
|
2463
|
+
*
|
|
2464
|
+
* - Write to `reply.raw` directly (NDJSON, custom SSE — historical contract).
|
|
2465
|
+
* - Return a Web `ReadableStream` (Vercel AI SDK's `result.toUIMessageStream()`,
|
|
2466
|
+
* `fetch().body`, etc.). 2.17.1+ auto-pipes these through
|
|
2467
|
+
* `pipeUIMessageStreamToReply()` so each chunk is JSON-encoded into
|
|
2468
|
+
* an SSE `data:` frame and client-disconnect cancels the source.
|
|
2469
|
+
* Pre-2.17.1 this crashed with `chunk must be a string or Buffer`.
|
|
2470
|
+
*/
|
|
2462
2471
|
readonly streamResponse?: boolean;
|
|
2472
|
+
/**
|
|
2473
|
+
* INTERNAL — marker stamped by built-in presets (softDelete, tree, …)
|
|
2474
|
+
* onto routes they emit. Read by the MCP collision detector
|
|
2475
|
+
* (`createMcpServer.resolveToolCollisions`) to attribute the route
|
|
2476
|
+
* source ("preset:softDelete vs user `actions.restore`") and to
|
|
2477
|
+
* auto-namespace the preset side when a user-authored tool legitimately
|
|
2478
|
+
* shadows it. Not part of the public surface — hosts must not set this.
|
|
2479
|
+
*
|
|
2480
|
+
* @internal
|
|
2481
|
+
*/
|
|
2482
|
+
readonly _presetSource?: string;
|
|
2483
|
+
/**
|
|
2484
|
+
* Apply the resource's `fields` (field-level write permissions) to this
|
|
2485
|
+
* custom route's request body.
|
|
2486
|
+
*
|
|
2487
|
+
* Default: `true` for body-bearing methods (POST/PUT/PATCH), `false`
|
|
2488
|
+
* otherwise. Set to `false` to opt out — handler accepts the body as
|
|
2489
|
+
* supplied with no field-write filtering. The resource-level
|
|
2490
|
+
* `onFieldWriteDenied: 'reject' | 'strip'` setting governs how denials
|
|
2491
|
+
* are surfaced.
|
|
2492
|
+
*
|
|
2493
|
+
* `raw: true` routes ALWAYS bypass this — they've opted out of the
|
|
2494
|
+
* pipeline entirely.
|
|
2495
|
+
*/
|
|
2496
|
+
readonly fieldWrite?: boolean;
|
|
2463
2497
|
/**
|
|
2464
2498
|
* Fastify route schema. Each slot (`body`, `querystring`, `params`,
|
|
2465
2499
|
* `headers`, `response[status]`) accepts a plain JSON Schema object
|
|
@@ -2941,6 +2975,86 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
2941
2975
|
skipValidation?: boolean;
|
|
2942
2976
|
/** Don't register in introspection */
|
|
2943
2977
|
skipRegistry?: boolean;
|
|
2978
|
+
/**
|
|
2979
|
+
* Default `limit` value when the request omits `?limit=`. Default 20.
|
|
2980
|
+
* Surfaced at the resource level (rather than only via a custom
|
|
2981
|
+
* `queryParser`) so hosts can declare it inline next to the resource
|
|
2982
|
+
* shape. Threads into both the auto-built controller's QueryResolver
|
|
2983
|
+
* AND the OpenAPI listQuery schema.
|
|
2984
|
+
*
|
|
2985
|
+
* For "fetch all" reference data, prefer the `referenceData: true`
|
|
2986
|
+
* shorthand below — it pins this AND `maxLimit` AND aggressive cache
|
|
2987
|
+
* defaults in one declaration.
|
|
2988
|
+
*/
|
|
2989
|
+
defaultLimit?: number;
|
|
2990
|
+
/**
|
|
2991
|
+
* URL-driven `limit` ceiling. Default 100. Requests with
|
|
2992
|
+
* `?limit=N` above this cap fail validation with a 400 that names
|
|
2993
|
+
* the cap (`Query parameter 'limit' must be <= 100 (got N)`,
|
|
2994
|
+
* `meta.cap: 100` on the wire envelope) — 2.17.0+ surfaces the cap
|
|
2995
|
+
* value programmatically so callers can self-correct.
|
|
2996
|
+
*/
|
|
2997
|
+
maxLimit?: number;
|
|
2998
|
+
/**
|
|
2999
|
+
* Mark this resource as **reference data** — small, mostly-static rows
|
|
3000
|
+
* (currencies, countries, plans, credentials, pipeline stages) that
|
|
3001
|
+
* callers want to fetch all of at once and cache aggressively.
|
|
3002
|
+
*
|
|
3003
|
+
* Sets the following defaults (each only when the corresponding
|
|
3004
|
+
* narrower flag is not explicitly set, so this stays composable):
|
|
3005
|
+
*
|
|
3006
|
+
* - `crud: { list: true, get: true }` — read-only surface (reference
|
|
3007
|
+
* data is rarely mutated through the public API; mutate via
|
|
3008
|
+
* migrations / admin tools).
|
|
3009
|
+
* - `defaultLimit: 1000` — one request returns everything by default.
|
|
3010
|
+
* - `maxLimit: 1000` — same cap so explicit `?limit=` requests
|
|
3011
|
+
* don't have to know the resource is reference data.
|
|
3012
|
+
* - `cache: { staleTime: 300, gcTime: 600 }` — 5 min fresh / 10 min
|
|
3013
|
+
* GC window so reference data isn't refetched on every request
|
|
3014
|
+
* (values are seconds per `ResourceCacheConfig`). No-op without
|
|
3015
|
+
* `queryCachePlugin` — same contract as the manual `cache` field;
|
|
3016
|
+
* a first-mount diagnostic fires when the plugin is missing.
|
|
3017
|
+
*
|
|
3018
|
+
* Pre-2.17.0 hosts achieved this by sprinkling `defaultLimit: 1000` +
|
|
3019
|
+
* a custom queryParser + a `cache:` block across every reference
|
|
3020
|
+
* resource. The shorthand collapses that to one flag.
|
|
3021
|
+
*
|
|
3022
|
+
* @example
|
|
3023
|
+
* ```ts
|
|
3024
|
+
* defineResource({
|
|
3025
|
+
* name: 'credential-type',
|
|
3026
|
+
* adapter,
|
|
3027
|
+
* referenceData: true,
|
|
3028
|
+
* });
|
|
3029
|
+
* ```
|
|
3030
|
+
*/
|
|
3031
|
+
referenceData?: boolean;
|
|
3032
|
+
/**
|
|
3033
|
+
* Shorthand for the "service resource" pattern — a resource that ships
|
|
3034
|
+
* ONLY custom routes (no auto-CRUD, no adapter, no validation, no
|
|
3035
|
+
* introspection registry entry). Setting `customRoutesOnly: true`
|
|
3036
|
+
* is equivalent to the three-flag combo:
|
|
3037
|
+
*
|
|
3038
|
+
* ```ts
|
|
3039
|
+
* { disableDefaultRoutes: true, skipValidation: true, skipRegistry: true }
|
|
3040
|
+
* ```
|
|
3041
|
+
*
|
|
3042
|
+
* Each of those flags stays public — power users that need to opt OUT
|
|
3043
|
+
* of one (e.g. `customRoutesOnly: true` + `skipRegistry: false` because
|
|
3044
|
+
* a custom-routes-only resource still wants OpenAPI docs) can pass the
|
|
3045
|
+
* narrow flag explicitly; explicit settings always win over the
|
|
3046
|
+
* shorthand. The shorthand only fills in flags that were not set.
|
|
3047
|
+
*
|
|
3048
|
+
* @example
|
|
3049
|
+
* ```ts
|
|
3050
|
+
* defineResource({
|
|
3051
|
+
* name: 'health',
|
|
3052
|
+
* customRoutesOnly: true,
|
|
3053
|
+
* routes: [{ method: 'GET', path: '/ping', handler: () => ({ ok: true }) }],
|
|
3054
|
+
* });
|
|
3055
|
+
* ```
|
|
3056
|
+
*/
|
|
3057
|
+
customRoutesOnly?: boolean;
|
|
2944
3058
|
/**
|
|
2945
3059
|
* Per-resource MCP opt-out.
|
|
2946
3060
|
*
|
|
@@ -3084,7 +3198,16 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
|
|
|
3084
3198
|
readonly updateMethod?: "PUT" | "PATCH" | "both";
|
|
3085
3199
|
readonly pipe?: PipelineConfig;
|
|
3086
3200
|
readonly fields?: FieldPermissionMap;
|
|
3201
|
+
/**
|
|
3202
|
+
* Policy for field-write denials — `'reject'` (default, secure) raises 403
|
|
3203
|
+
* listing the denied fields; `'strip'` silently drops them. Honored by
|
|
3204
|
+
* both `BodySanitizer` (auto-CRUD) and `buildFieldWritePreHandler`
|
|
3205
|
+
* (custom routes) so the policy is consistent across all body-bearing
|
|
3206
|
+
* routes for the resource.
|
|
3207
|
+
*/
|
|
3208
|
+
readonly onFieldWriteDenied?: "reject" | "strip";
|
|
3087
3209
|
readonly cache?: ResourceCacheConfig;
|
|
3210
|
+
readonly referenceData: boolean;
|
|
3088
3211
|
/**
|
|
3089
3212
|
* Per-resource MCP opt-out. `false` keeps the resource out of every
|
|
3090
3213
|
* `mcpPlugin` registration regardless of the plugin's `expose` /
|
|
@@ -3121,8 +3244,34 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
|
|
|
3121
3244
|
* `buildResourcePlugin` so the host's configured logger owns the
|
|
3122
3245
|
* output — the framework never speaks to `console.*` from `src/`
|
|
3123
3246
|
* outside of the CLI.
|
|
3247
|
+
*
|
|
3248
|
+
* Prefer the public {@link warnings} getter for programmatic access
|
|
3249
|
+
* (CI fail-on-warnings, lint integrations) — it returns a frozen
|
|
3250
|
+
* snapshot so callers can't accidentally mutate the diagnostic list.
|
|
3124
3251
|
*/
|
|
3125
3252
|
_diagnostics?: ResourceDiagnostic[];
|
|
3253
|
+
/**
|
|
3254
|
+
* Public view of {@link _diagnostics} for programmatic consumption.
|
|
3255
|
+
*
|
|
3256
|
+
* Returns the collected define-time diagnostics for this resource.
|
|
3257
|
+
* Empty when the resource validated cleanly. Useful for CI gates that
|
|
3258
|
+
* want to fail the build on any non-fatal warning:
|
|
3259
|
+
*
|
|
3260
|
+
* @example
|
|
3261
|
+
* ```ts
|
|
3262
|
+
* import { orderResource } from './resources/order.resource.js';
|
|
3263
|
+
* if (orderResource.warnings.length > 0) {
|
|
3264
|
+
* for (const w of orderResource.warnings) console.error(w.message);
|
|
3265
|
+
* process.exit(1);
|
|
3266
|
+
* }
|
|
3267
|
+
* ```
|
|
3268
|
+
*
|
|
3269
|
+
* **Scope.** Covers define-time only — diagnostics emitted at
|
|
3270
|
+
* first-mount (e.g. `cache.invalidateOn` without `queryCachePlugin`)
|
|
3271
|
+
* surface through `fastify.log.warn` instead and are not collected
|
|
3272
|
+
* here; they require a Fastify instance to detect.
|
|
3273
|
+
*/
|
|
3274
|
+
get warnings(): readonly ResourceDiagnostic[];
|
|
3126
3275
|
/**
|
|
3127
3276
|
* Per-host idempotency guard used by `buildResourcePlugin` to
|
|
3128
3277
|
* skip duplicate shared-state writes when the same resource is
|
|
@@ -3176,6 +3325,26 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
|
|
|
3176
3325
|
* `BaseController<TDoc extends AnyRecord>` actually requires it.
|
|
3177
3326
|
* One internal boundary cast replaces N host-side casts.
|
|
3178
3327
|
*/
|
|
3328
|
+
/**
|
|
3329
|
+
* Narrow overload: when the host passes an `actions: { ... }` literal, the
|
|
3330
|
+
* returned `ResourceDefinition.actions` is typed as the exact captured map
|
|
3331
|
+
* (not the wide `ActionsMap | undefined`). Without this overload, hosts that
|
|
3332
|
+
* wrote `defineResource({ actions: { send: ..., receive: ... } })` saw
|
|
3333
|
+
* `resource.actions` typed `ActionsMap | undefined` and got
|
|
3334
|
+
* "'resource.actions' is possibly 'undefined'" on every `resource.actions.send`
|
|
3335
|
+
* call. TS infers `TActions` from the object literal at the call site —
|
|
3336
|
+
* no explicit generic needed.
|
|
3337
|
+
*/
|
|
3338
|
+
declare function defineResource<TDoc, TActions extends ActionsMap>(config: ResourceConfig<TDoc> & {
|
|
3339
|
+
actions: TActions;
|
|
3340
|
+
}): ResourceDefinition<TDoc> & {
|
|
3341
|
+
readonly actions: TActions;
|
|
3342
|
+
};
|
|
3343
|
+
/**
|
|
3344
|
+
* Wide overload: no `actions` declared. `ResourceDefinition<TDoc>.actions`
|
|
3345
|
+
* stays `ActionsMap | undefined` exactly as in pre-2.17.1 — no
|
|
3346
|
+
* back-compat breakage for hosts that never touched actions.
|
|
3347
|
+
*/
|
|
3179
3348
|
declare function defineResource<TDoc = AnyRecord>(config: ResourceConfig<TDoc>): ResourceDefinition<TDoc>;
|
|
3180
3349
|
//#endregion
|
|
3181
3350
|
//#region src/registry/ResourceRegistry.d.ts
|
|
@@ -3445,6 +3614,13 @@ interface CrudRouterOptions {
|
|
|
3445
3614
|
schemaOptions?: RouteSchemaOptions;
|
|
3446
3615
|
/** Field-level permissions (visibility, writability per role) */
|
|
3447
3616
|
fields?: FieldPermissionMap;
|
|
3617
|
+
/**
|
|
3618
|
+
* Policy for handling fields the caller lacks write permission for.
|
|
3619
|
+
* Auto-CRUD reads this via `BodySanitizer`; custom routes' field-write
|
|
3620
|
+
* preHandler honors the same setting. Default: `'reject'` — silent
|
|
3621
|
+
* stripping hides misconfigurations and real attacks.
|
|
3622
|
+
*/
|
|
3623
|
+
onFieldWriteDenied?: "reject" | "strip";
|
|
3448
3624
|
/** HTTP method for update routes. Default: 'PATCH' */
|
|
3449
3625
|
updateMethod?: "PUT" | "PATCH" | "both";
|
|
3450
3626
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as RequestContext, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, bt as IRequestContext, ft as RouteMcpConfig, mt as RouteSchemaOptions, q as CrudController, st as ResourceConfig, vt as IController, yt as IControllerResponse } from "./index-
|
|
1
|
+
import { C as RequestContext, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, bt as IRequestContext, ft as RouteMcpConfig, mt as RouteSchemaOptions, q as CrudController, st as ResourceConfig, vt as IController, yt as IControllerResponse } from "./index-C1JSBan-.mjs";
|
|
2
2
|
import { i as RequestScope } from "./types-CfapW2t8.mjs";
|
|
3
3
|
import { c as PermissionCheck } from "./fields-DqZLp5d6.mjs";
|
|
4
4
|
import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $t as BaseController, A as RequestIdOptions, At as AggregationRateLimit, B as defineResource, C as RequestContext, Ct as AggMeasureShorthand, D as HealthCheck, Dt as AggregationIndexHint, E as GracefulShutdownOptions, Et as AggregationDateRangeRequirement, F as FastifyWithDecorators, J as CrudRouteKey, Jt as ArcRequest, Kt as AnyRecord, L as RequestWithExtras, N as FastifyRequestExtras, O as HealthOptions, Ot as AggregationMaterializedContext, P as FastifyWithAuth, Q as MiddlewareConfig, Qt as UserOrganization, S as QueryParserInterface, St as AggMeasureInput, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, Vt as AuthPluginOptions, X as EventDefinition, Y as CrudSchemas, Yt as JWTPayload, Z as FieldRule, _n as ArcListResult, a as InferAdapterDoc, an as SlugMixin, bt as IRequestContext, c as TypedController, cn as BaseControllerOptions, d as PaginationResult, dn as ControllerConstructionOptions, en as SoftDeleteExt, f as IntrospectionData, g as ArcInternalMetadata, gn as ArcGetResult, gt as ControllerLike, h as ResourceMetadata, hn as ArcDeleteResult, i as ValidationResult, in as SlugExt, it as RateLimitConfig, jt as AggregationsMap, k as IntrospectionPluginOptions, kt as AggregationMaterializedResult, l as TypedRepository, ln as BaseCrudController, m as RegistryStats, mn as ArcCreateResult, mt as RouteSchemaOptions, n as ConfigError, nn as TreeExt, o as InferDocType, on as BulkExt, p as RegistryEntry, q as CrudController, qt as ApiResponse, r as ValidateOptions, rn as TreeMixin, rt as PresetResult, s as InferResourceDoc, sn as BulkMixin, st as ResourceConfig, t as RouteHandlerMethod, tn as SoftDeleteMixin, tt as PresetFunction, u as TypedResourceConfig, un as ControllerConfigurableOptions, vn as ArcUpdateResult, vt as IController, w as ServiceContext, wt as AggregationCacheConfig, xt as RouteHandler, y as OwnershipCheck, yn as ListResult, yt as IControllerResponse } from "./index-
|
|
1
|
+
import { $t as BaseController, A as RequestIdOptions, At as AggregationRateLimit, B as defineResource, C as RequestContext, Ct as AggMeasureShorthand, D as HealthCheck, Dt as AggregationIndexHint, E as GracefulShutdownOptions, Et as AggregationDateRangeRequirement, F as FastifyWithDecorators, J as CrudRouteKey, Jt as ArcRequest, Kt as AnyRecord, L as RequestWithExtras, N as FastifyRequestExtras, O as HealthOptions, Ot as AggregationMaterializedContext, P as FastifyWithAuth, Q as MiddlewareConfig, Qt as UserOrganization, S as QueryParserInterface, St as AggMeasureInput, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, Vt as AuthPluginOptions, X as EventDefinition, Y as CrudSchemas, Yt as JWTPayload, Z as FieldRule, _n as ArcListResult, a as InferAdapterDoc, an as SlugMixin, bt as IRequestContext, c as TypedController, cn as BaseControllerOptions, d as PaginationResult, dn as ControllerConstructionOptions, en as SoftDeleteExt, f as IntrospectionData, g as ArcInternalMetadata, gn as ArcGetResult, gt as ControllerLike, h as ResourceMetadata, hn as ArcDeleteResult, i as ValidationResult, in as SlugExt, it as RateLimitConfig, jt as AggregationsMap, k as IntrospectionPluginOptions, kt as AggregationMaterializedResult, l as TypedRepository, ln as BaseCrudController, m as RegistryStats, mn as ArcCreateResult, mt as RouteSchemaOptions, n as ConfigError, nn as TreeExt, o as InferDocType, on as BulkExt, p as RegistryEntry, q as CrudController, qt as ApiResponse, r as ValidateOptions, rn as TreeMixin, rt as PresetResult, s as InferResourceDoc, sn as BulkMixin, st as ResourceConfig, t as RouteHandlerMethod, tn as SoftDeleteMixin, tt as PresetFunction, u as TypedResourceConfig, un as ControllerConfigurableOptions, vn as ArcUpdateResult, vt as IController, w as ServiceContext, wt as AggregationCacheConfig, xt as RouteHandler, y as OwnershipCheck, yn as ListResult, yt as IControllerResponse } from "./index-C1JSBan-.mjs";
|
|
2
2
|
import { a as applyFieldWritePermissions, c as PermissionCheck, d as UserBase, i as applyFieldReadPermissions, l as PermissionContext, n as FieldPermissionMap, o as fields, t as FieldPermission, u as PermissionResult } from "./fields-DqZLp5d6.mjs";
|
|
3
|
-
import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, b as DEFAULT_ID_FIELD, f as defineResourceVariants, j as MAX_REGEX_LENGTH, k as HookPhase, m as defineAction, s as getControllerScope, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "./index-
|
|
3
|
+
import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, b as DEFAULT_ID_FIELD, f as defineResourceVariants, j as MAX_REGEX_LENGTH, k as HookPhase, m as defineAction, s as getControllerScope, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "./index-CruZir1o.mjs";
|
|
4
4
|
import { A as requireRoles, C as allOf, E as denyAll, M as when, O as requireAuth, S as createOrgPermissions, T as anyOf, a as presets_d_exports, c as readOnly, d as requireOrgRole, f as requireScopeContext, i as ownerWithAdminBypass, k as requireOwnership, l as requireOrgInScope, m as requireTeamMembership, n as authenticated, o as publicRead, p as requireServiceScope, r as fullPublic, s as publicReadAdminWrite, t as adminOnly, u as requireOrgMembership, v as DynamicPermissionMatrix, w as allowPublic, x as createDynamicPermissionMatrix, y as DynamicPermissionMatrixConfig } from "./index-3L-adFJO.mjs";
|
|
5
5
|
import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-4ZloYFvm.mjs";
|
|
6
6
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "./constants-TrJVIJl0.mjs";
|
|
2
2
|
import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-j4aJm1Wg.mjs";
|
|
3
|
-
import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController-
|
|
4
|
-
import { C as
|
|
5
|
-
import {
|
|
6
|
-
import { a as defineResource, i as defineResourceVariants, o as ResourceDefinition, s as defineAction, u as defineAggregation } from "./core-
|
|
3
|
+
import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController-CgHomZ4d.mjs";
|
|
4
|
+
import { C as requireAuth, D as when, M as applyFieldWritePermissions, N as fields, T as requireRoles, _ as requireTeamMembership, a as presets_exports, b as anyOf, c as readOnly, d as createOrgPermissions, f as requireOrgInScope, g as requireServiceScope, h as requireScopeContext, i as ownerWithAdminBypass, j as applyFieldReadPermissions, m as requireOrgRole, n as authenticated, o as publicRead, p as requireOrgMembership, r as fullPublic, s as publicReadAdminWrite, t as adminOnly, u as createDynamicPermissionMatrix, v as allOf, w as requireOwnership, x as denyAll, y as allowPublic } from "./permissions-B1Qy55fg.mjs";
|
|
5
|
+
import { x as getControllerScope } from "./routerShared-nCyJGOKX.mjs";
|
|
6
|
+
import { a as defineResource, i as defineResourceVariants, o as ResourceDefinition, s as defineAction, u as defineAggregation } from "./core-D8tpQ4ZL.mjs";
|
|
7
7
|
//#region src/index.ts
|
|
8
|
-
const version = "2.
|
|
8
|
+
const version = "2.17.1";
|
|
9
9
|
//#endregion
|
|
10
10
|
export { ArcError, BaseController, BaseCrudController, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, NotFoundError, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, authenticated, createDomainError, createDynamicPermissionMatrix, createOrgPermissions, defineAction, defineAggregation, defineResource, defineResourceVariants, denyAll, fields, fullPublic, getControllerScope, ownerWithAdminBypass, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, version, when };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as WebSocketMessage, i as WebSocketClient, o as WebSocketPluginOptions } from "../websocket-BJ0w-3Dt.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-CoMjNmkF.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 };
|
|
@@ -39,6 +39,14 @@ interface JobDefinition<TData = unknown, TResult = unknown> {
|
|
|
39
39
|
deadLetterQueue?: string;
|
|
40
40
|
/** Repeat schedule — cron or interval. Requires explicit timezone for cron. */
|
|
41
41
|
repeat?: JobRepeatOptions;
|
|
42
|
+
/**
|
|
43
|
+
* Arc-level semaphore: max simultaneous handler executions for this job type.
|
|
44
|
+
* Jobs that dequeue while all slots are full are held (state stays `active`)
|
|
45
|
+
* and appear as `throttled: true` in `GET /jobs/:id/status`.
|
|
46
|
+
* Distinct from BullMQ `concurrency` — use this when the constraint is a
|
|
47
|
+
* downstream resource (AI model rate limit, DB connection pool, external API).
|
|
48
|
+
*/
|
|
49
|
+
maxConcurrent?: number;
|
|
42
50
|
}
|
|
43
51
|
interface JobMeta {
|
|
44
52
|
jobId: string;
|
|
@@ -59,6 +67,27 @@ interface JobDispatchOptions {
|
|
|
59
67
|
/** One-shot repeat override at dispatch time. Usually prefer `JobDefinition.repeat`. */
|
|
60
68
|
repeat?: JobRepeatOptions;
|
|
61
69
|
}
|
|
70
|
+
/** Point-in-time snapshot of a single job. */
|
|
71
|
+
interface JobStatus {
|
|
72
|
+
id: string;
|
|
73
|
+
/** Registered job name (= queue name). */
|
|
74
|
+
name: string;
|
|
75
|
+
state: "waiting" | "active" | "completed" | "failed" | "delayed" | "unknown";
|
|
76
|
+
/** Progress reported by the handler via BullMQ `job.updateProgress()`. */
|
|
77
|
+
progress: number | Record<string, unknown>;
|
|
78
|
+
/** True when the job is dequeued by a worker but blocked on a `maxConcurrent` semaphore slot. */
|
|
79
|
+
throttled?: boolean;
|
|
80
|
+
/** Unix ms when the job was enqueued. */
|
|
81
|
+
timestamp?: number;
|
|
82
|
+
/** Unix ms when processing started. */
|
|
83
|
+
processedOn?: number;
|
|
84
|
+
/** Unix ms when the job finished (completed or failed). */
|
|
85
|
+
finishedOn?: number;
|
|
86
|
+
/** Failure message when `state === 'failed'`. */
|
|
87
|
+
failedReason?: string;
|
|
88
|
+
/** Handler return value when `state === 'completed'` (only present if removeOnComplete hasn't cleared the job). */
|
|
89
|
+
returnValue?: unknown;
|
|
90
|
+
}
|
|
62
91
|
interface JobsPluginOptions {
|
|
63
92
|
/** Redis connection options (passed to BullMQ) */
|
|
64
93
|
connection: {
|
|
@@ -91,6 +120,12 @@ interface JobDispatcher {
|
|
|
91
120
|
}>;
|
|
92
121
|
getQueue(name: string): unknown | null;
|
|
93
122
|
getStats(): Promise<Record<string, QueueStats>>;
|
|
123
|
+
/**
|
|
124
|
+
* Fetch a point-in-time status snapshot for a job by its ID.
|
|
125
|
+
* Searches across all registered queues.
|
|
126
|
+
* Returns `null` if the job doesn't exist or has been removed by retention policy.
|
|
127
|
+
*/
|
|
128
|
+
getStatus(jobId: string): Promise<JobStatus | null>;
|
|
94
129
|
close(): Promise<void>;
|
|
95
130
|
}
|
|
96
131
|
interface QueueStats {
|
|
@@ -122,4 +157,4 @@ declare function defineJob<TData = unknown, TResult = unknown>(definition: JobDe
|
|
|
122
157
|
*/
|
|
123
158
|
declare const jobsPlugin: FastifyPluginAsync<JobsPluginOptions>;
|
|
124
159
|
//#endregion
|
|
125
|
-
export { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobRepeatOptions, JobsPluginOptions, QueueStats, defineJob, jobsPlugin };
|
|
160
|
+
export { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobRepeatOptions, JobStatus, JobsPluginOptions, QueueStats, defineJob, jobsPlugin };
|