@classytic/arc 2.8.4 → 2.9.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.
Files changed (130) hide show
  1. package/README.md +116 -5
  2. package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-Vu2yc56T.mjs} +188 -102
  3. package/dist/EventTransport-CqZ8FyM_.d.mts +293 -0
  4. package/dist/adapters/index.d.mts +2 -2
  5. package/dist/audit/index.d.mts +100 -11
  6. package/dist/audit/index.mjs +71 -18
  7. package/dist/auth/index.d.mts +15 -7
  8. package/dist/auth/index.mjs +13 -6
  9. package/dist/{betterAuthOpenApi-C5lDyRH2.mjs → betterAuthOpenApi--rdY15Ld.mjs} +1 -1
  10. package/dist/cache/index.d.mts +71 -1
  11. package/dist/cache/index.mjs +96 -3
  12. package/dist/cli/commands/docs.mjs +1 -1
  13. package/dist/cli/commands/generate.mjs +1 -1
  14. package/dist/core/index.d.mts +3 -3
  15. package/dist/core/index.mjs +4 -5
  16. package/dist/{core-DKSwNSXf.mjs → core-DNncu0xF.mjs} +1 -1
  17. package/dist/{createActionRouter-Df1BuawX.mjs → createActionRouter-DH1YFL9m.mjs} +3 -3
  18. package/dist/{createApp-BOYjBgdI.mjs → createApp-CBJUJKGP.mjs} +6 -5
  19. package/dist/{defineResource-Bb_Bdhtw.mjs → defineResource-C__jkwvs.mjs} +22 -57
  20. package/dist/docs/index.d.mts +1 -1
  21. package/dist/docs/index.mjs +1 -1
  22. package/dist/dynamic/index.d.mts +2 -2
  23. package/dist/dynamic/index.mjs +3 -3
  24. package/dist/{elevation-BBGFjzIP.mjs → elevation-DxQ6ACbt.mjs} +20 -6
  25. package/dist/{errorHandler-mzqk4cGl.mjs → errorHandler-CZDW4EXS.mjs} +59 -7
  26. package/dist/{errorHandler-CdZDavNH.d.mts → errorHandler-DixGcttC.d.mts} +37 -2
  27. package/dist/{eventPlugin-CVxlE6De.d.mts → eventPlugin-BxvaCIZF.d.mts} +14 -2
  28. package/dist/{eventPlugin-D91S2YF4.mjs → eventPlugin-Dl7MoVWH.mjs} +83 -5
  29. package/dist/events/index.d.mts +147 -36
  30. package/dist/events/index.mjs +338 -101
  31. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  32. package/dist/events/transports/redis.d.mts +1 -1
  33. package/dist/factory/index.d.mts +1 -1
  34. package/dist/factory/index.mjs +1 -1
  35. package/dist/{fields-DC4So2M2.d.mts → fields-BC7zcmI9.d.mts} +15 -3
  36. package/dist/{fields-ipsbIRPK.mjs → fields-CU6FlaDV.mjs} +18 -5
  37. package/dist/filesUpload-q8oHt--L.mjs +377 -0
  38. package/dist/hooks/index.d.mts +1 -1
  39. package/dist/idempotency/index.d.mts +28 -4
  40. package/dist/idempotency/index.mjs +111 -2
  41. package/dist/idempotency/redis.d.mts +2 -2
  42. package/dist/idempotency/redis.mjs +134 -13
  43. package/dist/{index-CSkeivBx.d.mts → index-C-xjcA6F.d.mts} +2 -2
  44. package/dist/{index-CpTSDqmD.d.mts → index-Cibkchnx.d.mts} +5 -136
  45. package/dist/{index-BgmMdpm8.d.mts → index-CtGKT0lf.d.mts} +1 -1
  46. package/dist/index.d.mts +8 -8
  47. package/dist/index.mjs +8 -8
  48. package/dist/integrations/event-gateway.d.mts +1 -1
  49. package/dist/integrations/index.d.mts +1 -1
  50. package/dist/integrations/jobs.d.mts +25 -3
  51. package/dist/integrations/jobs.mjs +63 -4
  52. package/dist/integrations/mcp/index.d.mts +26 -8
  53. package/dist/integrations/mcp/index.mjs +96 -17
  54. package/dist/integrations/mcp/testing.d.mts +1 -1
  55. package/dist/integrations/mcp/testing.mjs +1 -1
  56. package/dist/integrations/webhooks.d.mts +5 -0
  57. package/dist/integrations/webhooks.mjs +6 -0
  58. package/dist/{interface-BVuMfeVv.d.mts → interface-YrWsmKqE.d.mts} +324 -194
  59. package/dist/{openapi-CYCuekCn.mjs → openapi-CXuTG1M9.mjs} +3 -3
  60. package/dist/org/index.d.mts +2 -2
  61. package/dist/permissions/index.d.mts +3 -3
  62. package/dist/permissions/index.mjs +3 -3
  63. package/dist/{permissions-CH4cNwJi.mjs → permissions-oNZawnkR.mjs} +1 -1
  64. package/dist/plugins/index.d.mts +6 -6
  65. package/dist/plugins/index.mjs +4 -4
  66. package/dist/plugins/response-cache.mjs +1 -1
  67. package/dist/plugins/tracing-entry.d.mts +1 -1
  68. package/dist/plugins/tracing-entry.mjs +1 -1
  69. package/dist/policies/index.d.mts +26 -33
  70. package/dist/presets/filesUpload.d.mts +71 -0
  71. package/dist/presets/filesUpload.mjs +2 -0
  72. package/dist/presets/index.d.mts +4 -2
  73. package/dist/presets/index.mjs +4 -2
  74. package/dist/presets/multiTenant.d.mts +1 -1
  75. package/dist/presets/multiTenant.mjs +1 -1
  76. package/dist/presets/search.d.mts +91 -0
  77. package/dist/presets/search.mjs +150 -0
  78. package/dist/{presets-C2xgzW6x.mjs → presets-hM4WhNWY.mjs} +1 -1
  79. package/dist/{queryCachePlugin-D0iIVhW_.mjs → queryCachePlugin-DbUVroUG.mjs} +2 -2
  80. package/dist/redis-MXLp1oOf.d.mts +115 -0
  81. package/dist/{redis-stream-D54N5oXs.d.mts → redis-stream-Bz-4q96t.d.mts} +1 -1
  82. package/dist/registry/index.d.mts +1 -1
  83. package/dist/{resourceToTools-O_HwWXFa.mjs → resourceToTools-C3cWymnW.mjs} +65 -48
  84. package/dist/rpc/index.mjs +1 -1
  85. package/dist/{schemaConverter-OxfCshus.mjs → schemaConverter-BxFDdtXu.mjs} +25 -9
  86. package/dist/scope/index.d.mts +2 -2
  87. package/dist/scope/index.mjs +1 -1
  88. package/dist/storage-BwGQXUpd.d.mts +146 -0
  89. package/dist/store-helpers-DFiZl5TL.mjs +57 -0
  90. package/dist/testing/index.d.mts +7 -15
  91. package/dist/testing/index.mjs +23 -76
  92. package/dist/testing/storageContract.d.mts +26 -0
  93. package/dist/testing/storageContract.mjs +216 -0
  94. package/dist/types/index.d.mts +5 -5
  95. package/dist/types/storage.d.mts +2 -0
  96. package/dist/types/storage.mjs +1 -0
  97. package/dist/{types-CcG4avic.d.mts → types-CoSzA-s-.d.mts} +1 -1
  98. package/dist/{types-Bg2X42_m.d.mts → types-CunEX4UX.d.mts} +7 -5
  99. package/dist/{types-CVC4HOKi.d.mts → types-DZi1aYhm.d.mts} +1 -1
  100. package/dist/utils/index.d.mts +26 -8
  101. package/dist/utils/index.mjs +6 -6
  102. package/dist/{utils-yYT3HDXt.mjs → utils-B7FuRr9w.mjs} +1 -1
  103. package/package.json +23 -11
  104. package/skills/arc/SKILL.md +92 -14
  105. package/skills/arc/references/auth.md +94 -0
  106. package/skills/arc/references/events.md +229 -12
  107. package/skills/arc/references/mcp.md +4 -17
  108. package/skills/arc/references/multi-tenancy.md +43 -0
  109. package/skills/arc/references/production.md +34 -19
  110. package/dist/EventTransport-CinyO7zQ.d.mts +0 -135
  111. package/dist/audit/mongodb.d.mts +0 -2
  112. package/dist/audit/mongodb.mjs +0 -2
  113. package/dist/idempotency/mongodb.d.mts +0 -2
  114. package/dist/idempotency/mongodb.mjs +0 -123
  115. package/dist/mongodb-B5O6xaW1.mjs +0 -90
  116. package/dist/mongodb-B8U2xaLj.d.mts +0 -127
  117. package/dist/mongodb-X7LbEjTN.d.mts +0 -80
  118. package/dist/redis-z3sFr1UP.d.mts +0 -49
  119. /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-bqGpo9ML.mjs} +0 -0
  120. /package/dist/{circuitBreaker-cmi5XDv5.mjs → circuitBreaker-l18oRgL5.mjs} +0 -0
  121. /package/dist/{elevation-s5ykdNHr.d.mts → elevation-B6S5csVA.d.mts} +0 -0
  122. /package/dist/{errors-Bmn3eZT6.d.mts → errors-BI8kEKsO.d.mts} +0 -0
  123. /package/dist/{errors-BF2bIOIS.mjs → errors-CqWnSqM-.mjs} +0 -0
  124. /package/dist/{memory-Cp7_cAko.mjs → memory-BFAYkf8H.mjs} +0 -0
  125. /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-CWP6MB39.mjs} +0 -0
  126. /package/dist/{queryParser-CgCtsjti.mjs → queryParser-Cs-6SHQK.mjs} +0 -0
  127. /package/dist/{requestContext-DYvHl113.mjs → requestContext-DYtmNpm5.mjs} +0 -0
  128. /package/dist/{tracing-DxjKk7eW.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
  129. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
  130. /package/dist/{types-C72d3NDn.d.mts → types-BD85MlEK.d.mts} +0 -0
@@ -77,18 +77,24 @@ var RedisIdempotencyStore = class {
77
77
  }
78
78
  async findByPrefix(prefix) {
79
79
  const keys = await this.scanByPrefix(this.resultKey(prefix));
80
- for (const key of keys) {
81
- const data = await this.client.get(key);
82
- if (!data) continue;
83
- try {
84
- const result = JSON.parse(data);
85
- if (new Date(result.expiresAt) < /* @__PURE__ */ new Date()) continue;
86
- return {
87
- ...result,
88
- createdAt: new Date(result.createdAt),
89
- expiresAt: new Date(result.expiresAt)
90
- };
91
- } catch {}
80
+ if (keys.length === 0) return void 0;
81
+ const BATCH_SIZE = 10;
82
+ const now = /* @__PURE__ */ new Date();
83
+ for (let i = 0; i < keys.length; i += BATCH_SIZE) {
84
+ const batch = keys.slice(i, i + BATCH_SIZE);
85
+ const values = await Promise.all(batch.map((k) => this.client.get(k)));
86
+ for (const data of values) {
87
+ if (!data) continue;
88
+ try {
89
+ const result = JSON.parse(data);
90
+ if (new Date(result.expiresAt) < now) continue;
91
+ return {
92
+ ...result,
93
+ createdAt: new Date(result.createdAt),
94
+ expiresAt: new Date(result.expiresAt)
95
+ };
96
+ } catch {}
97
+ }
92
98
  }
93
99
  }
94
100
  /** Scan Redis keys matching a prefix pattern. Falls back to empty if SCAN unavailable. */
@@ -105,5 +111,120 @@ var RedisIdempotencyStore = class {
105
111
  }
106
112
  async close() {}
107
113
  };
114
+ /**
115
+ * Wrap an ioredis instance as the arc idempotency `RedisClient`.
116
+ *
117
+ * Arc's idempotency store expects node-redis-v4 style option objects
118
+ * (`{ EX, NX }`). ioredis uses positional flags. This adapter lets users
119
+ * plug an ioredis instance in without writing the bridge themselves.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * import Redis from 'ioredis';
124
+ * import { RedisIdempotencyStore, ioredisAsIdempotencyClient }
125
+ * from '@classytic/arc/idempotency/redis';
126
+ *
127
+ * const redis = new Redis(process.env.REDIS_URL);
128
+ * const store = new RedisIdempotencyStore({
129
+ * client: ioredisAsIdempotencyClient(redis),
130
+ * });
131
+ * ```
132
+ */
133
+ function ioredisAsIdempotencyClient(client) {
134
+ return {
135
+ async get(key) {
136
+ return client.get(key);
137
+ },
138
+ async set(key, value, options) {
139
+ const args = [key, value];
140
+ if (options?.EX != null) args.push("EX", options.EX);
141
+ if (options?.NX) args.push("NX");
142
+ return client.set(...args);
143
+ },
144
+ async del(key) {
145
+ if (Array.isArray(key)) return client.del(...key);
146
+ return client.del(key);
147
+ },
148
+ async exists(key) {
149
+ if (Array.isArray(key)) return client.exists(...key);
150
+ return client.exists(key);
151
+ },
152
+ async scan(cursor, ...args) {
153
+ const [next, keys] = await client.scan(cursor, ...args);
154
+ return [next, keys];
155
+ },
156
+ eval: client.eval ? (script, numKeys, ...args) => client.eval(script, numKeys, ...args) : void 0,
157
+ async quit() {
158
+ if (client.quit) return client.quit();
159
+ return "OK";
160
+ },
161
+ async disconnect() {
162
+ if (client.disconnect) client.disconnect();
163
+ }
164
+ };
165
+ }
166
+ /**
167
+ * Wrap an `@upstash/redis` REST client as an idempotency `RedisClient`.
168
+ *
169
+ * Enables running arc's idempotency store on Cloudflare Workers, Vercel Edge
170
+ * and Deno Deploy — runtimes that don't support raw TCP (ioredis).
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * import { Redis } from '@upstash/redis';
175
+ * import { RedisIdempotencyStore, upstashAsIdempotencyClient }
176
+ * from '@classytic/arc/idempotency/redis';
177
+ *
178
+ * const redis = Redis.fromEnv();
179
+ * const store = new RedisIdempotencyStore({
180
+ * client: upstashAsIdempotencyClient(redis),
181
+ * });
182
+ * ```
183
+ */
184
+ function upstashAsIdempotencyClient(client) {
185
+ return {
186
+ async get(key) {
187
+ const raw = await client.get(key);
188
+ if (raw == null) return null;
189
+ return typeof raw === "string" ? raw : JSON.stringify(raw);
190
+ },
191
+ async set(key, value, options) {
192
+ const opts = {};
193
+ if (options?.EX != null) opts.ex = options.EX;
194
+ if (options?.NX) opts.nx = true;
195
+ const res = await client.set(key, value, opts);
196
+ return res == null ? null : String(res);
197
+ },
198
+ async del(key) {
199
+ if (Array.isArray(key)) return client.del(...key);
200
+ return client.del(key);
201
+ },
202
+ async exists(key) {
203
+ if (Array.isArray(key)) return client.exists(...key);
204
+ return client.exists(key);
205
+ },
206
+ async scan(cursor, ...args) {
207
+ const opts = {};
208
+ for (let i = 0; i < args.length; i += 2) {
209
+ const flag = String(args[i]).toLowerCase();
210
+ const val = args[i + 1];
211
+ if (flag === "match" && typeof val === "string") opts.match = val;
212
+ if (flag === "count") opts.count = Number(val);
213
+ }
214
+ const [next, keys] = await client.scan(cursor, opts);
215
+ return [next, keys];
216
+ },
217
+ eval: client.eval ? async (script, _numKeys, ...args) => {
218
+ const keyCount = _numKeys;
219
+ const keys = args.slice(0, keyCount).map(String);
220
+ const rest = args.slice(keyCount);
221
+ return client.eval(script, keys, rest);
222
+ } : void 0,
223
+ async quit() {
224
+ return "OK";
225
+ },
226
+ async disconnect() {}
227
+ };
228
+ }
108
229
  //#endregion
109
- export { RedisIdempotencyStore };
230
+ export { RedisIdempotencyStore, ioredisAsIdempotencyClient, upstashAsIdempotencyClient };
@@ -1,5 +1,5 @@
1
- import { r as RequestScope } from "./types-C72d3NDn.mjs";
2
- import { n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-CVC4HOKi.mjs";
1
+ import { r as RequestScope } from "./types-BD85MlEK.mjs";
2
+ import { n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-DZi1aYhm.mjs";
3
3
  import { i as CacheStore, t as CacheLogger } from "./interface-DplgQO2e.mjs";
4
4
  import { FastifyRequest } from "fastify";
5
5
 
@@ -1,7 +1,7 @@
1
- import { r as RequestScope } from "./types-C72d3NDn.mjs";
2
- import { D as CrudRouterOptions, Ht as IControllerResponse, N as FastifyWithDecorators, T as CrudController, Ut as IRequestContext, Vt as IController, ct as RequestWithExtras, m as AnyRecord, ot as RequestContext, qt as ResourceDefinition, ut as ResourceConfig } from "./interface-BVuMfeVv.mjs";
3
- import { t as PermissionCheck } from "./types-CVC4HOKi.mjs";
4
- import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
1
+ import { r as RequestScope } from "./types-BD85MlEK.mjs";
2
+ import { Bt as IController, E as CrudRouterOptions, Ht as IRequestContext, Kt as ResourceDefinition, M as FastifyWithDecorators, Vt as IControllerResponse, at as RequestContext, lt as ResourceConfig, p as AnyRecord, w as CrudController } from "./interface-YrWsmKqE.mjs";
3
+ import { t as PermissionCheck } from "./types-DZi1aYhm.mjs";
4
+ import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
5
5
 
6
6
  //#region src/constants.d.ts
7
7
  /**
@@ -52,137 +52,6 @@ declare const MAX_FILTER_DEPTH: 10;
52
52
  */
53
53
  declare const RESERVED_QUERY_PARAMS: Readonly<Set<string>>;
54
54
  //#endregion
55
- //#region src/core/createActionRouter.d.ts
56
- /**
57
- * Action handler function
58
- * @param id - Resource ID
59
- * @param data - Action-specific data from request body
60
- * @param req - Full Fastify request object
61
- * @returns Action result (will be wrapped in success response)
62
- */
63
- type ActionHandler<TData = Record<string, unknown>, TResult = unknown> = (id: string, data: TData, req: RequestWithExtras) => Promise<TResult>;
64
- /**
65
- * Action router configuration
66
- */
67
- interface ActionRouterConfig {
68
- /**
69
- * OpenAPI tag for grouping routes
70
- */
71
- readonly tag?: string;
72
- /**
73
- * Action handlers map
74
- * @example { approve: (id, data, req) => service.approve(id), ... }
75
- */
76
- readonly actions: Record<string, ActionHandler>;
77
- /**
78
- * Per-action permission checks (PermissionCheck functions)
79
- * @example { approve: requireRoles(['admin', 'manager']), cancel: requireRoles(['admin']) }
80
- */
81
- readonly actionPermissions?: Record<string, PermissionCheck>;
82
- /**
83
- * Per-action schema for body validation.
84
- *
85
- * Accepted shapes per action:
86
- *
87
- * 1. **Full JSON Schema object** with `type: 'object'`, `properties`, `required` —
88
- * used verbatim. Required fields ARE enforced by Fastify's AJV.
89
- *
90
- * 2. **Zod v4 schema** — auto-converted via `z.toJSONSchema()`. Required fields
91
- * ARE enforced.
92
- *
93
- * 3. **Legacy field map** `{ fieldName: { type: 'string' } }` — each key becomes
94
- * a property and is treated as REQUIRED unless the property schema has
95
- * `nullable: true` or Arc's sentinel `required: false`. Kept for back-compat.
96
- *
97
- * All shapes are compiled into a single `oneOf` discriminator body schema
98
- * so AJV can validate action-specific required fields at the HTTP layer.
99
- *
100
- * @example JSON Schema
101
- * ```ts
102
- * actionSchemas: {
103
- * dispatch: {
104
- * type: 'object',
105
- * properties: { carrier: { type: 'string' } },
106
- * required: ['carrier'],
107
- * },
108
- * }
109
- * ```
110
- *
111
- * @example Zod v4
112
- * ```ts
113
- * actionSchemas: {
114
- * dispatch: z.object({ carrier: z.string() }),
115
- * }
116
- * ```
117
- */
118
- readonly actionSchemas?: Record<string, Record<string, unknown>>;
119
- /**
120
- * Global permission check applied to all actions (if action-specific not defined)
121
- */
122
- readonly globalAuth?: PermissionCheck;
123
- /**
124
- * Optional idempotency service
125
- * If provided, will handle idempotency-key header
126
- */
127
- readonly idempotencyService?: IdempotencyService;
128
- /**
129
- * Custom error handler for action execution failures
130
- * @param error - The error thrown by action handler
131
- * @param action - The action that failed
132
- * @param id - The resource ID
133
- * @returns Status code and error response
134
- */
135
- readonly onError?: (error: Error, action: string, id: string) => {
136
- statusCode: number;
137
- error: string;
138
- code?: string;
139
- };
140
- }
141
- /**
142
- * Idempotency service interface
143
- * Apps can provide their own implementation
144
- */
145
- interface IdempotencyService {
146
- check(key: string, payload: unknown): Promise<{
147
- isNew: boolean;
148
- existingResult?: unknown;
149
- }>;
150
- complete(key: string | undefined, result: unknown): Promise<void>;
151
- fail(key: string | undefined, error: Error): Promise<void>;
152
- }
153
- /**
154
- * Create action-based state transition endpoint
155
- *
156
- * Registers: POST /:id/action
157
- * Body: { action: string, ...actionData }
158
- *
159
- * @param fastify - Fastify instance
160
- * @param config - Action router configuration
161
- */
162
- declare function createActionRouter(fastify: FastifyInstance, config: ActionRouterConfig): void;
163
- /**
164
- * Build a discriminated body schema for the unified action endpoint.
165
- *
166
- * Produces a schema of the form:
167
- * ```json
168
- * {
169
- * "type": "object",
170
- * "required": ["action"],
171
- * "oneOf": [
172
- * { "properties": { "action": { "const": "dispatch" }, "carrier": {...} }, "required": ["action", "carrier"] },
173
- * { "properties": { "action": { "const": "approve" } }, "required": ["action"] }
174
- * ]
175
- * }
176
- * ```
177
- *
178
- * AJV validates this natively, so an action call missing required fields is
179
- * rejected with HTTP 400 before the handler ever runs.
180
- *
181
- * Exported so OpenAPI generation and MCP tool generation can reuse the same
182
- * schema shape (single source of truth).
183
- */
184
- declare function buildActionBodySchema(actionEnum: readonly string[], actionSchemas?: Record<string, Record<string, unknown>>): Record<string, unknown>;
185
- //#endregion
186
55
  //#region src/core/createCrudRouter.d.ts
187
56
  /**
188
57
  * Create CRUD routes for a controller
@@ -309,4 +178,4 @@ declare function createCrudHandlers<TDoc>(controller: IController<TDoc>): {
309
178
  delete: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
310
179
  };
311
180
  //#endregion
312
- export { MUTATION_OPERATIONS as A, HOOK_OPERATIONS as C, MAX_FILTER_DEPTH as D, HookPhase as E, RESERVED_QUERY_PARAMS as M, SYSTEM_FIELDS as N, MAX_REGEX_LENGTH as O, DEFAULT_UPDATE_METHOD as S, HookOperation as T, DEFAULT_ID_FIELD as _, getControllerScope as a, DEFAULT_SORT as b, createCrudRouter as c, ActionRouterConfig as d, IdempotencyService as f, CrudOperation as g, CRUD_OPERATIONS as h, getControllerContext as i, MutationOperation as j, MAX_SEARCH_LENGTH as k, createPermissionMiddleware as l, createActionRouter as m, createFastifyHandler as n, sendControllerResponse as o, buildActionBodySchema as p, createRequestContext as r, defineResourceVariants as s, createCrudHandlers as t, ActionHandler as u, DEFAULT_LIMIT as v, HOOK_PHASES as w, DEFAULT_TENANT_FIELD as x, DEFAULT_MAX_LIMIT as y };
181
+ export { MAX_REGEX_LENGTH as C, RESERVED_QUERY_PARAMS as D, MutationOperation as E, SYSTEM_FIELDS as O, MAX_FILTER_DEPTH as S, MUTATION_OPERATIONS as T, DEFAULT_UPDATE_METHOD as _, getControllerScope as a, HookOperation as b, createCrudRouter as c, CrudOperation as d, DEFAULT_ID_FIELD as f, DEFAULT_TENANT_FIELD as g, DEFAULT_SORT as h, getControllerContext as i, createPermissionMiddleware as l, DEFAULT_MAX_LIMIT as m, createFastifyHandler as n, sendControllerResponse as o, DEFAULT_LIMIT as p, createRequestContext as r, defineResourceVariants as s, createCrudHandlers as t, CRUD_OPERATIONS as u, HOOK_OPERATIONS as v, MAX_SEARCH_LENGTH as w, HookPhase as x, HOOK_PHASES as y };
@@ -1,4 +1,4 @@
1
- import { Y as OpenApiSchemas, Z as ParsedQuery, Zt as CrudRepository, c as ValidationResult, n as AdapterSchemaContext, nt as QueryParserInterface, o as RepositoryLike, r as DataAdapter, s as SchemaMetadata, vt as RouteSchemaOptions } from "./interface-BVuMfeVv.mjs";
1
+ import { J as OpenApiSchemas, X as ParsedQuery, Xt as CrudRepository, _t as RouteSchemaOptions, c as ValidationResult, n as AdapterSchemaContext, o as RepositoryLike, r as DataAdapter, s as SchemaMetadata, tt as QueryParserInterface } from "./interface-YrWsmKqE.mjs";
2
2
  import { Model } from "mongoose";
3
3
 
4
4
  //#region src/adapters/mongoose.d.ts
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { $ as PresetFunction, $t as DeleteOptions, At as BaseController, B as InferResourceDoc, C as ConfigError, Ct as TypedResourceConfig, D as CrudRouterOptions, Dt as ValidationResult$1, E as CrudRouteKey, Et as ValidateOptions, F as GracefulShutdownOptions, H as IntrospectionPluginOptions, Ht as IControllerResponse, I as HealthCheck, Jt as defineResource, K as MiddlewareConfig, L as HealthOptions, M as FastifyWithAuth, N as FastifyWithDecorators, O as CrudSchemas, Ot as envelope, P as FieldRule, Qt as DeleteManyResult, R as InferAdapterDoc, St as TypedRepository, T as CrudController, Tt as UserOrganization, U as JWTPayload, Ut as IRequestContext, V as IntrospectionData, Vt as IController, Wt as RouteHandler, X as OwnershipCheck, Xt as BulkWriteResult, Yt as BulkWriteOperation, Zt as CrudRepository, _ as ArcInternalMetadata, _n as PipelineStep, a as RelationMetadata, an as PaginationParams, at as RegistryStats, b as AuthPluginOptions, c as ValidationResult, cn as RepositorySession, ct as RequestWithExtras, dn as Guard, en as DeleteResult, fn as Interceptor, gn as PipelineContext, gt as RouteHandlerMethod, h as ApiResponse, hn as PipelineConfig, i as FieldMetadata, in as PaginatedResult, it as RegistryEntry, j as FastifyRequestExtras, jt as BaseControllerOptions, k as EventDefinition, ln as UpdateManyResult, m as AnyRecord, mn as OperationFilter, nn as KeysetPaginatedResult, o as RepositoryLike, on as PaginationResult, ot as RequestContext, p as AdditionalRoute, pn as NextFunction, pt as ResourceMetadata, q as MiddlewareHandler, qt as ResourceDefinition, r as DataAdapter, rn as OffsetPaginatedResult, rt as RateLimitConfig, s as SchemaMetadata, sn as QueryOptions, st as RequestIdOptions, tt as PresetResult, un as WriteOptions, ut as ResourceConfig, v as ArcRequest, vn as Transform, vt as RouteSchemaOptions, xt as TypedController, yt as ServiceContext, z as InferDocType, zt as ControllerLike } from "./interface-BVuMfeVv.mjs";
2
- import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-DC4So2M2.mjs";
3
- import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-CVC4HOKi.mjs";
4
- import { l as createMongooseAdapter, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./index-BgmMdpm8.mjs";
5
- import { A as MUTATION_OPERATIONS, C as HOOK_OPERATIONS, D as MAX_FILTER_DEPTH, E as HookPhase, M as RESERVED_QUERY_PARAMS, N as SYSTEM_FIELDS, O as MAX_REGEX_LENGTH, S as DEFAULT_UPDATE_METHOD, T as HookOperation, _ as DEFAULT_ID_FIELD, a as getControllerScope, b as DEFAULT_SORT, g as CrudOperation, h as CRUD_OPERATIONS, j as MutationOperation, k as MAX_SEARCH_LENGTH, s as defineResourceVariants, v as DEFAULT_LIMIT, w as HOOK_PHASES, x as DEFAULT_TENANT_FIELD, y as DEFAULT_MAX_LIMIT } from "./index-CpTSDqmD.mjs";
6
- import { C as authenticated, D as publicRead, E as presets_d_exports, O as publicReadAdminWrite, S as adminOnly, T as ownerWithAdminBypass, _ as requireScopeContext, a as allOf, c as createDynamicPermissionMatrix, d as requireAuth, f as requireOrgInScope, g as requireRoles, h as requireOwnership, k as readOnly, l as createOrgPermissions, m as requireOrgRole, n as DynamicPermissionMatrix, o as allowPublic, p as requireOrgMembership, r as DynamicPermissionMatrixConfig, s as anyOf, u as denyAll, v as requireServiceScope, w as fullPublic, x as when, y as requireTeamMembership } from "./index-CSkeivBx.mjs";
7
- import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-Bmn3eZT6.mjs";
1
+ import { $t as DeleteResult, A as FastifyRequestExtras, At as BaseControllerOptions, B as IntrospectionData, Bt as IController, D as CrudSchemas, Dt as envelope, E as CrudRouterOptions, Et as ValidationResult$1, F as HealthCheck, G as MiddlewareConfig, H as JWTPayload, Ht as IRequestContext, I as HealthOptions, Jt as BulkWriteOperation, K as MiddlewareHandler, Kt as ResourceDefinition, L as InferAdapterDoc, M as FastifyWithDecorators, N as FieldRule, O as EventDefinition, P as GracefulShutdownOptions, Q as PresetFunction, Qt as DeleteOptions, R as InferDocType, Rt as ControllerLike, S as ConfigError, St as TypedResourceConfig, T as CrudRouteKey, Tt as ValidateOptions, Ut as RouteHandler, V as IntrospectionPluginOptions, Vt as IControllerResponse, Xt as CrudRepository, Y as OwnershipCheck, Yt as BulkWriteResult, Zt as DeleteManyResult, _ as ArcRequest, _n as PipelineStep, _t as RouteSchemaOptions, a as RelationMetadata, an as PaginationParams, at as RequestContext, bt as TypedController, c as ValidationResult, cn as RepositorySession, dn as Guard, et as PresetResult, fn as Interceptor, ft as ResourceMetadata, g as ArcInternalMetadata, gn as PipelineContext, hn as PipelineConfig, ht as RouteHandlerMethod, i as FieldMetadata, in as PaginatedResult, it as RegistryStats, j as FastifyWithAuth, kt as BaseController, ln as UpdateManyResult, lt as ResourceConfig, m as ApiResponse, mn as OperationFilter, nn as KeysetPaginatedResult, nt as RateLimitConfig, o as RepositoryLike, on as PaginationResult, ot as RequestIdOptions, p as AnyRecord, pn as NextFunction, qt as defineResource, r as DataAdapter, rn as OffsetPaginatedResult, rt as RegistryEntry, s as SchemaMetadata, sn as QueryOptions, st as RequestWithExtras, un as WriteOptions, vn as Transform, vt as ServiceContext, w as CrudController, wt as UserOrganization, xt as TypedRepository, y as AuthPluginOptions, z as InferResourceDoc } from "./interface-YrWsmKqE.mjs";
2
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-BC7zcmI9.mjs";
3
+ import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-DZi1aYhm.mjs";
4
+ import { l as createMongooseAdapter, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./index-CtGKT0lf.mjs";
5
+ import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, m as DEFAULT_MAX_LIMIT, p as DEFAULT_LIMIT, s as defineResourceVariants, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "./index-Cibkchnx.mjs";
6
+ import { C as authenticated, D as publicRead, E as presets_d_exports, O as publicReadAdminWrite, S as adminOnly, T as ownerWithAdminBypass, _ as requireScopeContext, a as allOf, c as createDynamicPermissionMatrix, d as requireAuth, f as requireOrgInScope, g as requireRoles, h as requireOwnership, k as readOnly, l as createOrgPermissions, m as requireOrgRole, n as DynamicPermissionMatrix, o as allowPublic, p as requireOrgMembership, r as DynamicPermissionMatrixConfig, s as anyOf, u as denyAll, v as requireServiceScope, w as fullPublic, x as when, y as requireTeamMembership } from "./index-C-xjcA6F.mjs";
7
+ import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-BI8kEKsO.mjs";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import { RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
10
10
 
@@ -253,4 +253,4 @@ declare function arcLog(module: string): ArcLogger;
253
253
  //#region src/index.d.ts
254
254
  declare const version: string;
255
255
  //#endregion
256
- export { type ValidationResult as AdapterValidationResult, type AdditionalRoute, type AnyRecord, type ApiResponse, ArcError, type ArcInternalMetadata, type ArcLogWriter, type ArcLogger, type ArcLoggerOptions, type ArcRequest, type AuthPluginOptions, BaseController, type BaseControllerOptions, type BulkWriteOperation, type BulkWriteResult, CRUD_OPERATIONS, type ConfigError, type ControllerLike, type CrudController, CrudOperation, type CrudRepository, type CrudRouteKey, type CrudRouterOptions, type CrudSchemas, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DataAdapter, type DeleteManyResult, type DeleteOptions, type DeleteResult, type DynamicPermissionMatrix, type DynamicPermissionMatrixConfig, type EventDefinition, type FastifyRequestExtras, type FastifyWithAuth, type FastifyWithDecorators, type FieldMetadata, type FieldPermission, type FieldPermissionMap, type FieldRule, ForbiddenError, type GracefulShutdownOptions, type Guard, HOOK_OPERATIONS, HOOK_PHASES, type HealthCheck, type HealthOptions, HookOperation, HookPhase, type IController, type IControllerResponse, type IRequestContext, type InferAdapterDoc, type InferDocType, type InferResourceDoc, type Interceptor, type IntrospectionData, type IntrospectionPluginOptions, type JWTPayload, type KeysetPaginatedResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, type MiddlewareConfig, MongooseAdapter, MutationOperation, type NamedMiddleware, NotFoundError, type OffsetPaginatedResult, type OwnershipCheck, type PaginatedResult, type PaginationParams, type PaginationResult, type PermissionCheck, type PermissionContext, type PermissionResult, type PipelineConfig, type PipelineContext, type PipelineStep, type PresetFunction, type PresetResult, PrismaAdapter, type QueryOptions, RESERVED_QUERY_PARAMS, type RateLimitConfig, type RegistryEntry, type RegistryStats, type RelationMetadata, type RepositoryLike, type RepositorySession, type RequestContext, type RequestIdOptions, type RequestStore, type RequestWithExtras, type ResourceConfig, ResourceDefinition, type ResourceMetadata, type RouteHandler, type RouteHandlerMethod, type RouteSchemaOptions, SYSTEM_FIELDS, type SchemaMetadata, type ServiceContext, type Transform, type TypedController, type TypedRepository, type TypedResourceConfig, UnauthorizedError, type UpdateManyResult, type UserBase, type UserOrganization, type ValidateOptions, ValidationError, type ValidationResult$1 as ValidationResult, type WriteOptions, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_d_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
256
+ export { type ValidationResult as AdapterValidationResult, type AnyRecord, type ApiResponse, ArcError, type ArcInternalMetadata, type ArcLogWriter, type ArcLogger, type ArcLoggerOptions, type ArcRequest, type AuthPluginOptions, BaseController, type BaseControllerOptions, type BulkWriteOperation, type BulkWriteResult, CRUD_OPERATIONS, type ConfigError, type ControllerLike, type CrudController, CrudOperation, type CrudRepository, type CrudRouteKey, type CrudRouterOptions, type CrudSchemas, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DataAdapter, type DeleteManyResult, type DeleteOptions, type DeleteResult, type DynamicPermissionMatrix, type DynamicPermissionMatrixConfig, type EventDefinition, type FastifyRequestExtras, type FastifyWithAuth, type FastifyWithDecorators, type FieldMetadata, type FieldPermission, type FieldPermissionMap, type FieldRule, ForbiddenError, type GracefulShutdownOptions, type Guard, HOOK_OPERATIONS, HOOK_PHASES, type HealthCheck, type HealthOptions, HookOperation, HookPhase, type IController, type IControllerResponse, type IRequestContext, type InferAdapterDoc, type InferDocType, type InferResourceDoc, type Interceptor, type IntrospectionData, type IntrospectionPluginOptions, type JWTPayload, type KeysetPaginatedResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, type MiddlewareConfig, MongooseAdapter, MutationOperation, type NamedMiddleware, NotFoundError, type OffsetPaginatedResult, type OwnershipCheck, type PaginatedResult, type PaginationParams, type PaginationResult, type PermissionCheck, type PermissionContext, type PermissionResult, type PipelineConfig, type PipelineContext, type PipelineStep, type PresetFunction, type PresetResult, PrismaAdapter, type QueryOptions, RESERVED_QUERY_PARAMS, type RateLimitConfig, type RegistryEntry, type RegistryStats, type RelationMetadata, type RepositoryLike, type RepositorySession, type RequestContext, type RequestIdOptions, type RequestStore, type RequestWithExtras, type ResourceConfig, ResourceDefinition, type ResourceMetadata, type RouteHandler, type RouteHandlerMethod, type RouteSchemaOptions, SYSTEM_FIELDS, type SchemaMetadata, type ServiceContext, type Transform, type TypedController, type TypedRepository, type TypedResourceConfig, UnauthorizedError, type UpdateManyResult, type UserBase, type UserOrganization, type ValidateOptions, ValidationError, type ValidationResult$1 as ValidationResult, type WriteOptions, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_d_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
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-Cxde4rpC.mjs";
2
2
  import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./adapters-BBqAVvPK.mjs";
3
- import { t as BaseController } from "./BaseController-DAGGc5Xn.mjs";
3
+ import { t as BaseController } from "./BaseController-Vu2yc56T.mjs";
4
4
  import { envelope } from "./types/index.mjs";
5
- import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-ipsbIRPK.mjs";
6
- import { t as requestContext } from "./requestContext-DYvHl113.mjs";
7
- import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-BF2bIOIS.mjs";
8
- import { a as validateResourceConfig, f as getControllerScope, i as formatValidationErrors, m as pipe, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-Bb_Bdhtw.mjs";
9
- import { C as publicRead, S as presets_exports, T as readOnly, _ as when, a as createOrgPermissions, b as fullPublic, c as requireOrgInScope, d as requireOwnership, f as requireRoles, h as requireTeamMembership, i as createDynamicPermissionMatrix, l as requireOrgMembership, m as requireServiceScope, n as allowPublic, o as denyAll, p as requireScopeContext, r as anyOf, s as requireAuth, t as allOf, u as requireOrgRole, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "./permissions-CH4cNwJi.mjs";
10
- import { t as defineResourceVariants } from "./core-DKSwNSXf.mjs";
5
+ import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-CU6FlaDV.mjs";
6
+ import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-CqWnSqM-.mjs";
7
+ import { t as requestContext } from "./requestContext-DYtmNpm5.mjs";
8
+ import { a as validateResourceConfig, f as getControllerScope, i as formatValidationErrors, m as pipe, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-C__jkwvs.mjs";
9
+ import { C as publicRead, S as presets_exports, T as readOnly, _ as when, a as createOrgPermissions, b as fullPublic, c as requireOrgInScope, d as requireOwnership, f as requireRoles, h as requireTeamMembership, i as createDynamicPermissionMatrix, l as requireOrgMembership, m as requireServiceScope, n as allowPublic, o as denyAll, p as requireScopeContext, r as anyOf, s as requireAuth, t as allOf, u as requireOrgRole, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "./permissions-oNZawnkR.mjs";
10
+ import { t as defineResourceVariants } from "./core-DNncu0xF.mjs";
11
11
  import { n as configureArcLogger, t as arcLog } from "./logger-CDjpjySd.mjs";
12
12
  //#region src/middleware/middleware.ts
13
13
  /**
@@ -128,6 +128,6 @@ function transform(name, handlerOrOptions) {
128
128
  }
129
129
  //#endregion
130
130
  //#region src/index.ts
131
- const version = "2.8.4";
131
+ const version = "2.9.1";
132
132
  //#endregion
133
133
  export { ArcError, BaseController, 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, MongooseAdapter, NotFoundError, PrismaAdapter, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
@@ -1,4 +1,4 @@
1
- import { t as DomainEvent } from "../EventTransport-CinyO7zQ.mjs";
1
+ import { n as DomainEvent } from "../EventTransport-CqZ8FyM_.mjs";
2
2
  import { WebSocketClient, WebSocketMessage } from "./websocket.mjs";
3
3
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
4
4
 
@@ -1,7 +1,7 @@
1
1
  import { WebSocketClient, WebSocketMessage, WebSocketPluginOptions } from "./websocket.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 McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-CcG4avic.mjs";
4
+ import { c as McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-CoSzA-s-.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 };
@@ -1,6 +1,19 @@
1
1
  import { FastifyPluginAsync } from "fastify";
2
2
 
3
3
  //#region src/integrations/jobs.d.ts
4
+ /** Repeat schedule — cron pattern or fixed interval. Explicit timezone is required. */
5
+ interface JobRepeatOptions {
6
+ /** Cron pattern (e.g. '0 9 * * *' = every day 09:00). Mutually exclusive with `every`. */
7
+ pattern?: string;
8
+ /** Fixed interval in ms. Mutually exclusive with `pattern`. */
9
+ every?: number;
10
+ /** IANA timezone (e.g. 'UTC', 'America/New_York'). Required for `pattern` — prevents DST drift. */
11
+ tz?: string;
12
+ /** Stop repeating after this date. */
13
+ endDate?: Date | string | number;
14
+ /** Max total runs. */
15
+ limit?: number;
16
+ }
4
17
  interface JobDefinition<TData = unknown, TResult = unknown> {
5
18
  /** Unique job name */
6
19
  name: string;
@@ -22,8 +35,10 @@ interface JobDefinition<TData = unknown, TResult = unknown> {
22
35
  max: number;
23
36
  duration: number;
24
37
  };
25
- /** Dead letter queue name (default: '{name}:dead') */
38
+ /** Dead letter queue name (default: '{name}-dead') */
26
39
  deadLetterQueue?: string;
40
+ /** Repeat schedule — cron or interval. Requires explicit timezone for cron. */
41
+ repeat?: JobRepeatOptions;
27
42
  }
28
43
  interface JobMeta {
29
44
  jobId: string;
@@ -41,6 +56,8 @@ interface JobDispatchOptions {
41
56
  removeOnComplete?: boolean | number;
42
57
  /** Remove job after failure */
43
58
  removeOnFail?: boolean | number;
59
+ /** One-shot repeat override at dispatch time. Usually prefer `JobDefinition.repeat`. */
60
+ repeat?: JobRepeatOptions;
44
61
  }
45
62
  interface JobsPluginOptions {
46
63
  /** Redis connection options (passed to BullMQ) */
@@ -97,7 +114,12 @@ interface QueueStats {
97
114
  * });
98
115
  */
99
116
  declare function defineJob<TData = unknown, TResult = unknown>(definition: JobDefinition<TData, TResult>): JobDefinition<TData, TResult>;
100
- /** Pluggable BullMQ job queue integration for Arc */
117
+ /**
118
+ * Pluggable BullMQ job queue integration for Arc.
119
+ *
120
+ * Wrapped with fastify-plugin so the `fastify.jobs` decorator is available
121
+ * in the outer scope (the documented `fastify.jobs.dispatch(...)` usage).
122
+ */
101
123
  declare const jobsPlugin: FastifyPluginAsync<JobsPluginOptions>;
102
124
  //#endregion
103
- export { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats, defineJob, jobsPlugin };
125
+ export { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobRepeatOptions, JobsPluginOptions, QueueStats, defineJob, jobsPlugin };
@@ -1,3 +1,4 @@
1
+ import fp from "fastify-plugin";
1
2
  //#region src/integrations/jobs.ts
2
3
  /**
3
4
  * Define a background job with typed data and configuration.
@@ -26,16 +27,41 @@ const jobsPluginImpl = async (fastify, options) => {
26
27
  } catch {
27
28
  throw new Error("@classytic/arc/integrations/jobs requires \"bullmq\" package.\nInstall it: npm install bullmq");
28
29
  }
30
+ if (connection && typeof connection === "object" && "options" in connection) {
31
+ if (connection.options?.maxRetriesPerRequest !== null) fastify.log.warn("[arc/jobs] BullMQ requires ioredis `maxRetriesPerRequest: null`. Pass `new Redis(url, { maxRetriesPerRequest: null, enableReadyCheck: false })` or workers will stall on transient Redis errors.");
32
+ }
29
33
  const queues = /* @__PURE__ */ new Map();
30
34
  const dlqQueues = /* @__PURE__ */ new Map();
31
35
  const workers = /* @__PURE__ */ new Map();
36
+ for (const job of jobs) {
37
+ if (!job.repeat) continue;
38
+ const { pattern, every, tz } = job.repeat;
39
+ if (pattern && every) throw new Error(`[arc/jobs] Job '${job.name}' sets both repeat.pattern and repeat.every — use one.`);
40
+ if (!pattern && every == null) throw new Error(`[arc/jobs] Job '${job.name}' has repeat config but no pattern or every.`);
41
+ if (pattern && !tz) throw new Error(`[arc/jobs] Job '${job.name}' uses a cron pattern but no timezone. Set repeat.tz (e.g. 'UTC' or 'America/New_York') to avoid DST drift.`);
42
+ }
32
43
  for (const job of jobs) {
33
44
  const queueName = job.name;
34
45
  const queue = new Queue(queueName, { connection });
35
46
  queues.set(queueName, queue);
47
+ if (job.repeat) {
48
+ const repeatOpts = {
49
+ ...job.repeat.pattern ? {
50
+ pattern: job.repeat.pattern,
51
+ tz: job.repeat.tz
52
+ } : { every: job.repeat.every },
53
+ ...job.repeat.endDate ? { endDate: job.repeat.endDate } : {},
54
+ ...job.repeat.limit != null ? { limit: job.repeat.limit } : {}
55
+ };
56
+ await queue.add(queueName, {}, {
57
+ repeat: repeatOpts,
58
+ removeOnComplete: defaults.removeOnComplete ?? 100,
59
+ removeOnFail: defaults.removeOnFail ?? 500
60
+ });
61
+ }
36
62
  let dlqQueue = null;
37
63
  if (job.deadLetterQueue != null) {
38
- const dlqName = job.deadLetterQueue || `${queueName}:dead`;
64
+ const dlqName = job.deadLetterQueue || `${queueName}-dead`;
39
65
  dlqQueue = new Queue(dlqName, { connection });
40
66
  dlqQueues.set(dlqName, dlqQueue);
41
67
  }
@@ -110,12 +136,35 @@ const jobsPluginImpl = async (fastify, options) => {
110
136
  }, `Failed to publish job.${queueName}.failed event`);
111
137
  }
112
138
  });
139
+ worker.on("stalled", async (jobId) => {
140
+ fastify.log.warn({
141
+ jobId,
142
+ queue: queueName
143
+ }, "Job stalled — worker may have crashed");
144
+ if (bridgeEvents && fastify.events?.publish) try {
145
+ await fastify.events.publish(`job.${queueName}.stalled`, { jobId });
146
+ } catch (err) {
147
+ fastify.log.warn({
148
+ err,
149
+ jobId
150
+ }, `Failed to publish job.${queueName}.stalled event`);
151
+ }
152
+ });
113
153
  workers.set(queueName, worker);
114
154
  }
155
+ const JOB_PAYLOAD_WARN_BYTES = 100 * 1024;
115
156
  const dispatcher = {
116
157
  async dispatch(name, data, opts = {}) {
117
158
  const queue = queues.get(name);
118
159
  if (!queue) throw new Error(`Job queue '${name}' not registered. Available: ${Array.from(queues.keys()).join(", ")}`);
160
+ try {
161
+ const serializedBytes = Buffer.byteLength(JSON.stringify(data) ?? "", "utf8");
162
+ if (serializedBytes > JOB_PAYLOAD_WARN_BYTES) fastify.log.warn({
163
+ queue: name,
164
+ bytes: serializedBytes,
165
+ limit: JOB_PAYLOAD_WARN_BYTES
166
+ }, `[arc/jobs] Large job payload — prefer passing IDs and reloading in the handler`);
167
+ } catch {}
119
168
  const jobDef = jobs.find((j) => j.name === name);
120
169
  return { jobId: (await queue.add(name, data, {
121
170
  delay: opts.delay,
@@ -127,7 +176,8 @@ const jobsPluginImpl = async (fastify, options) => {
127
176
  backoff: jobDef?.backoff ?? defaults.backoff ?? {
128
177
  type: "exponential",
129
178
  delay: 1e3
130
- }
179
+ },
180
+ repeat: jobDef?.repeat ?? opts.repeat
131
181
  })).id };
132
182
  },
133
183
  getQueue(name) {
@@ -148,6 +198,7 @@ const jobsPluginImpl = async (fastify, options) => {
148
198
  return stats;
149
199
  },
150
200
  async close() {
201
+ await Promise.all(Array.from(workers.values()).map((w) => w.pause().catch(() => {})));
151
202
  const closePromises = [];
152
203
  for (const worker of workers.values()) closePromises.push(worker.close());
153
204
  for (const queue of queues.values()) closePromises.push(queue.close());
@@ -166,7 +217,15 @@ const jobsPluginImpl = async (fastify, options) => {
166
217
  await dispatcher.close();
167
218
  });
168
219
  };
169
- /** Pluggable BullMQ job queue integration for Arc */
170
- const jobsPlugin = jobsPluginImpl;
220
+ /**
221
+ * Pluggable BullMQ job queue integration for Arc.
222
+ *
223
+ * Wrapped with fastify-plugin so the `fastify.jobs` decorator is available
224
+ * in the outer scope (the documented `fastify.jobs.dispatch(...)` usage).
225
+ */
226
+ const jobsPlugin = fp(jobsPluginImpl, {
227
+ name: "arc-jobs",
228
+ fastify: "5.x"
229
+ });
171
230
  //#endregion
172
231
  export { defineJob, jobsPlugin };