@classytic/arc 2.2.5 → 2.4.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 (174) hide show
  1. package/README.md +187 -18
  2. package/bin/arc.js +11 -3
  3. package/dist/BaseController-CkM5dUh_.mjs +1031 -0
  4. package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
  5. package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
  6. package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
  7. package/dist/adapters/index.d.mts +3 -5
  8. package/dist/adapters/index.mjs +2 -3
  9. package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
  10. package/dist/audit/index.d.mts +4 -7
  11. package/dist/audit/index.mjs +2 -29
  12. package/dist/audit/mongodb.d.mts +1 -4
  13. package/dist/audit/mongodb.mjs +2 -3
  14. package/dist/auth/index.d.mts +7 -9
  15. package/dist/auth/index.mjs +65 -63
  16. package/dist/auth/redis-session.d.mts +1 -1
  17. package/dist/auth/redis-session.mjs +1 -2
  18. package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
  19. package/dist/cache/index.d.mts +23 -23
  20. package/dist/cache/index.mjs +4 -6
  21. package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
  22. package/dist/chunk-BpYLSNr0.mjs +14 -0
  23. package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
  24. package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
  25. package/dist/cli/commands/describe.mjs +24 -7
  26. package/dist/cli/commands/docs.mjs +6 -7
  27. package/dist/cli/commands/doctor.d.mts +10 -0
  28. package/dist/cli/commands/doctor.mjs +156 -0
  29. package/dist/cli/commands/generate.mjs +66 -17
  30. package/dist/cli/commands/init.mjs +315 -45
  31. package/dist/cli/commands/introspect.mjs +2 -4
  32. package/dist/cli/index.d.mts +1 -10
  33. package/dist/cli/index.mjs +4 -153
  34. package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
  35. package/dist/core/index.d.mts +3 -5
  36. package/dist/core/index.mjs +5 -4
  37. package/dist/core-C1XCMtqM.mjs +185 -0
  38. package/dist/{createApp-BKHSl2nT.mjs → createApp-ByWNRsZj.mjs} +65 -36
  39. package/dist/{defineResource-DO9ONe_D.mjs → defineResource-D9aY5Cy6.mjs} +154 -1165
  40. package/dist/discovery/index.mjs +37 -5
  41. package/dist/docs/index.d.mts +6 -9
  42. package/dist/docs/index.mjs +3 -21
  43. package/dist/dynamic/index.d.mts +93 -0
  44. package/dist/dynamic/index.mjs +122 -0
  45. package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
  46. package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
  47. package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
  48. package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
  49. package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
  50. package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
  51. package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
  52. package/dist/events/index.d.mts +72 -7
  53. package/dist/events/index.mjs +216 -4
  54. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  55. package/dist/events/transports/redis-stream-entry.mjs +19 -7
  56. package/dist/events/transports/redis.d.mts +1 -1
  57. package/dist/events/transports/redis.mjs +3 -4
  58. package/dist/factory/index.d.mts +23 -9
  59. package/dist/factory/index.mjs +48 -3
  60. package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
  61. package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
  62. package/dist/hooks/index.d.mts +1 -3
  63. package/dist/hooks/index.mjs +2 -3
  64. package/dist/idempotency/index.d.mts +5 -5
  65. package/dist/idempotency/index.mjs +3 -7
  66. package/dist/idempotency/mongodb.d.mts +1 -1
  67. package/dist/idempotency/mongodb.mjs +4 -5
  68. package/dist/idempotency/redis.d.mts +1 -1
  69. package/dist/idempotency/redis.mjs +2 -5
  70. package/dist/{fastifyAdapter-CyAA2zlB.d.mts → index-BL8CaQih.d.mts} +56 -57
  71. package/dist/index-Diqcm14c.d.mts +369 -0
  72. package/dist/{prisma-xjhMEq_S.d.mts → index-yhxyjqNb.d.mts} +4 -5
  73. package/dist/index.d.mts +100 -105
  74. package/dist/index.mjs +85 -58
  75. package/dist/integrations/event-gateway.d.mts +1 -1
  76. package/dist/integrations/event-gateway.mjs +8 -4
  77. package/dist/integrations/index.d.mts +4 -2
  78. package/dist/integrations/index.mjs +1 -1
  79. package/dist/integrations/jobs.d.mts +2 -2
  80. package/dist/integrations/jobs.mjs +63 -14
  81. package/dist/integrations/mcp/index.d.mts +219 -0
  82. package/dist/integrations/mcp/index.mjs +572 -0
  83. package/dist/integrations/mcp/testing.d.mts +53 -0
  84. package/dist/integrations/mcp/testing.mjs +104 -0
  85. package/dist/integrations/streamline.mjs +39 -19
  86. package/dist/integrations/webhooks.d.mts +56 -0
  87. package/dist/integrations/webhooks.mjs +139 -0
  88. package/dist/integrations/websocket-redis.d.mts +46 -0
  89. package/dist/integrations/websocket-redis.mjs +50 -0
  90. package/dist/integrations/websocket.d.mts +68 -2
  91. package/dist/integrations/websocket.mjs +96 -13
  92. package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
  93. package/dist/interface-DGmPxakH.d.mts +2213 -0
  94. package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
  95. package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
  96. package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
  97. package/dist/metrics-Csh4nsvv.mjs +224 -0
  98. package/dist/migrations/index.mjs +3 -7
  99. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  100. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  101. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  102. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  103. package/dist/org/index.d.mts +12 -14
  104. package/dist/org/index.mjs +92 -119
  105. package/dist/org/types.d.mts +2 -2
  106. package/dist/org/types.mjs +1 -1
  107. package/dist/permissions/index.d.mts +4 -278
  108. package/dist/permissions/index.mjs +4 -579
  109. package/dist/permissions-CA5zg0yK.mjs +751 -0
  110. package/dist/plugins/index.d.mts +104 -107
  111. package/dist/plugins/index.mjs +203 -313
  112. package/dist/plugins/response-cache.mjs +4 -69
  113. package/dist/plugins/tracing-entry.d.mts +1 -1
  114. package/dist/plugins/tracing-entry.mjs +24 -11
  115. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  116. package/dist/policies/index.d.mts +2 -2
  117. package/dist/policies/index.mjs +80 -83
  118. package/dist/presets/index.d.mts +26 -19
  119. package/dist/presets/index.mjs +2 -142
  120. package/dist/presets/multiTenant.d.mts +1 -4
  121. package/dist/presets/multiTenant.mjs +4 -6
  122. package/dist/presets-C9QXJV1u.mjs +422 -0
  123. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  124. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  125. package/dist/queryParser-CgCtsjti.mjs +352 -0
  126. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  127. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  128. package/dist/registry/index.d.mts +1 -4
  129. package/dist/registry/index.mjs +3 -4
  130. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  131. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  132. package/dist/resourceToTools-B6ZN9Ing.mjs +489 -0
  133. package/dist/rpc/index.d.mts +90 -0
  134. package/dist/rpc/index.mjs +248 -0
  135. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  136. package/dist/schemas/index.d.mts +30 -30
  137. package/dist/schemas/index.mjs +4 -6
  138. package/dist/scope/index.d.mts +13 -2
  139. package/dist/scope/index.mjs +18 -5
  140. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  141. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  142. package/dist/testing/index.d.mts +551 -567
  143. package/dist/testing/index.mjs +1744 -1799
  144. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  145. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  146. package/dist/types/index.d.mts +4 -946
  147. package/dist/types/index.mjs +2 -4
  148. package/dist/types-BJmgxNbF.d.mts +275 -0
  149. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  150. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  151. package/dist/{types-DMSBMkaZ.d.mts → types-Dt0-AI6E.d.mts} +85 -27
  152. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  153. package/dist/utils/index.d.mts +255 -352
  154. package/dist/utils/index.mjs +7 -6
  155. package/dist/utils-Dc0WhlIl.mjs +594 -0
  156. package/dist/versioning-BzfeHmhj.mjs +37 -0
  157. package/package.json +46 -12
  158. package/skills/arc/SKILL.md +506 -0
  159. package/skills/arc/references/auth.md +250 -0
  160. package/skills/arc/references/events.md +272 -0
  161. package/skills/arc/references/integrations.md +385 -0
  162. package/skills/arc/references/mcp.md +386 -0
  163. package/skills/arc/references/production.md +610 -0
  164. package/skills/arc/references/testing.md +183 -0
  165. package/dist/audited-CGdLiSlE.mjs +0 -140
  166. package/dist/chunk-C7Uep-_p.mjs +0 -20
  167. package/dist/circuitBreaker-DYhWBW_D.mjs +0 -1096
  168. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  169. package/dist/interface-DZYNK9bb.d.mts +0 -1112
  170. package/dist/presets-BTeYbw7h.d.mts +0 -57
  171. package/dist/presets-CeFtfDR8.mjs +0 -119
  172. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  173. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  174. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
package/dist/index.mjs CHANGED
@@ -1,48 +1,95 @@
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-DdXFXQtN.mjs";
2
- import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./prisma-DJbMt3yf.mjs";
3
- import { a as validateResourceConfig, g as BaseController, i as formatValidationErrors, l as pipe, m as getControllerScope, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-DO9ONe_D.mjs";
4
- import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-CTd_CrKr.mjs";
5
- import { i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-DBANPbGr.mjs";
6
- import { t as requestContext } from "./requestContext-xi6OKBL-.mjs";
7
- import { a as presets_exports, c as readOnly, i as ownerWithAdminBypass, n as authenticated, o as publicRead, r as fullPublic, s as publicReadAdminWrite, t as adminOnly } from "./presets-CeFtfDR8.mjs";
8
- import { allOf, allowPublic, anyOf, createDynamicPermissionMatrix, createOrgPermissions, denyAll, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, when } from "./permissions/index.mjs";
9
- import { n as configureArcLogger, t as arcLog } from "./logger-ByrvQWZO.mjs";
10
-
11
- //#region src/pipeline/guard.ts
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
+ import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./adapters-DTC4Ug66.mjs";
3
+ import { t as BaseController } from "./BaseController-CkM5dUh_.mjs";
4
+ import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-ipsbIRPK.mjs";
5
+ import { t as requestContext } from "./requestContext-DYtmNpm5.mjs";
6
+ import { i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-rxhfP7Hf.mjs";
7
+ import { a as validateResourceConfig, f as getControllerScope, i as formatValidationErrors, m as pipe, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-D9aY5Cy6.mjs";
8
+ import { _ as ownerWithAdminBypass, a as createOrgPermissions, b as publicReadAdminWrite, c as requireOrgMembership, d as requireRoles, f as requireTeamMembership, g as fullPublic, h as authenticated, i as createDynamicPermissionMatrix, l as requireOrgRole, m as adminOnly, n as allowPublic, o as denyAll, p as when, r as anyOf, s as requireAuth, t as allOf, u as requireOwnership, v as presets_exports, x as readOnly, y as publicRead } from "./permissions-CA5zg0yK.mjs";
9
+ import { n as configureArcLogger, t as arcLog } from "./logger-Dz3j1ItV.mjs";
10
+ //#region src/middleware/middleware.ts
12
11
  /**
13
- * Create a named guard.
12
+ * Named Middleware Priority-based, conditional middleware execution.
14
13
  *
15
- * @param name - Guard name (for debugging/introspection)
16
- * @param handlerOrOptions - Handler function or options object
14
+ * Named middleware replaces flat arrays with structured, inspectable middleware
15
+ * that runs in priority order and supports conditional execution.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { middleware } from '@classytic/arc';
20
+ *
21
+ * const verifyEmail = middleware('verifyEmail', {
22
+ * operations: ['create', 'update'],
23
+ * priority: 5,
24
+ * when: (req) => !req.user?.emailVerified,
25
+ * handler: async (req, reply) => {
26
+ * reply.code(403).send({ error: 'Email verification required' });
27
+ * },
28
+ * });
29
+ *
30
+ * const rateLimit = middleware('rateLimit', {
31
+ * priority: 1,
32
+ * handler: async (req, reply) => {
33
+ * // rate limit logic
34
+ * },
35
+ * });
36
+ *
37
+ * const productResource = defineResource({
38
+ * name: 'product',
39
+ * adapter,
40
+ * middlewares: sortMiddlewares([verifyEmail, rateLimit]),
41
+ * });
42
+ * ```
17
43
  */
18
- function guard(name, handlerOrOptions) {
19
- const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
44
+ /**
45
+ * Create a named middleware with priority and conditions.
46
+ */
47
+ function middleware(name, options) {
20
48
  return {
21
- _type: "guard",
22
49
  name,
23
- operations: opts.operations,
24
- handler: opts.handler
50
+ operations: options.operations,
51
+ priority: options.priority ?? 10,
52
+ when: options.when,
53
+ handler: options.handler
25
54
  };
26
55
  }
27
-
56
+ /**
57
+ * Sort named middlewares by priority (ascending — lower runs first).
58
+ * Returns a MiddlewareConfig map keyed by operation, ready to pass to `defineResource()`.
59
+ */
60
+ function sortMiddlewares(middlewares) {
61
+ const sorted = [...middlewares].sort((a, b) => a.priority - b.priority);
62
+ const operations = CRUD_OPERATIONS;
63
+ const result = {};
64
+ for (const op of operations) {
65
+ const applicable = sorted.filter((m) => !m.operations || m.operations.length === 0 || m.operations.includes(op));
66
+ if (applicable.length > 0) result[op] = applicable.map((m) => {
67
+ if (!m.when) return m.handler;
68
+ const wrapped = async (request, reply) => {
69
+ if (await m.when?.(request)) return m.handler(request, reply);
70
+ };
71
+ return wrapped;
72
+ });
73
+ }
74
+ return result;
75
+ }
28
76
  //#endregion
29
- //#region src/pipeline/transform.ts
77
+ //#region src/pipeline/guard.ts
30
78
  /**
31
- * Create a named transform.
79
+ * Create a named guard.
32
80
  *
33
- * @param name - Transform name (for debugging/introspection)
81
+ * @param name - Guard name (for debugging/introspection)
34
82
  * @param handlerOrOptions - Handler function or options object
35
83
  */
36
- function transform(name, handlerOrOptions) {
84
+ function guard(name, handlerOrOptions) {
37
85
  const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
38
86
  return {
39
- _type: "transform",
87
+ _type: "guard",
40
88
  name,
41
89
  operations: opts.operations,
42
90
  handler: opts.handler
43
91
  };
44
92
  }
45
-
46
93
  //#endregion
47
94
  //#region src/pipeline/intercept.ts
48
95
  /**
@@ -60,45 +107,25 @@ function intercept(name, handlerOrOptions) {
60
107
  handler: opts.handler
61
108
  };
62
109
  }
63
-
64
110
  //#endregion
65
- //#region src/middleware/middleware.ts
111
+ //#region src/pipeline/transform.ts
66
112
  /**
67
- * Create a named middleware with priority and conditions.
113
+ * Create a named transform.
114
+ *
115
+ * @param name - Transform name (for debugging/introspection)
116
+ * @param handlerOrOptions - Handler function or options object
68
117
  */
69
- function middleware(name, options) {
118
+ function transform(name, handlerOrOptions) {
119
+ const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
70
120
  return {
121
+ _type: "transform",
71
122
  name,
72
- operations: options.operations,
73
- priority: options.priority ?? 10,
74
- when: options.when,
75
- handler: options.handler
123
+ operations: opts.operations,
124
+ handler: opts.handler
76
125
  };
77
126
  }
78
- /**
79
- * Sort named middlewares by priority (ascending — lower runs first).
80
- * Returns a MiddlewareConfig map keyed by operation, ready to pass to `defineResource()`.
81
- */
82
- function sortMiddlewares(middlewares) {
83
- const sorted = [...middlewares].sort((a, b) => a.priority - b.priority);
84
- const operations = CRUD_OPERATIONS;
85
- const result = {};
86
- for (const op of operations) {
87
- const applicable = sorted.filter((m) => !m.operations || m.operations.length === 0 || m.operations.includes(op));
88
- if (applicable.length > 0) result[op] = applicable.map((m) => {
89
- if (!m.when) return m.handler;
90
- const wrapped = async (request, reply) => {
91
- if (await m.when(request)) return m.handler(request, reply);
92
- };
93
- return wrapped;
94
- });
95
- }
96
- return result;
97
- }
98
-
99
127
  //#endregion
100
128
  //#region src/index.ts
101
- const version = "__ARC_VERSION__";
102
-
129
+ const version = "2.4.1";
103
130
  //#endregion
104
- 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, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, denyAll, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
131
+ 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, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, denyAll, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
@@ -1,4 +1,4 @@
1
- import { t as DomainEvent } from "../EventTransport-BkUDYZEb.mjs";
1
+ import { t as DomainEvent } from "../EventTransport-wc5hSLik.mjs";
2
2
  import { WebSocketClient, WebSocketMessage } from "./websocket.mjs";
3
3
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
4
4
 
@@ -1,11 +1,10 @@
1
1
  import fp from "fastify-plugin";
2
-
3
2
  //#region src/integrations/event-gateway.ts
4
3
  const eventGatewayPluginImpl = async (fastify, opts = {}) => {
5
4
  const { auth = true, orgScoped = false, roomPolicy, maxMessageBytes, maxSubscriptionsPerClient, authenticate } = opts;
6
5
  if (auth && !authenticate && !fastify.hasDecorator("authenticate")) throw new Error("[arc-event-gateway] auth is true but fastify.authenticate is not registered. Register an auth plugin first, provide a custom authenticate function, or set auth: false.");
7
6
  if (opts.sse !== false) {
8
- const { default: ssePlugin } = await import("../sse-DkqQ1uxb.mjs").then((n) => n.r);
7
+ const { default: ssePlugin } = await import("../sse-BkViJPlT.mjs").then((n) => n.r);
9
8
  await fastify.register(ssePlugin, {
10
9
  path: opts.sse?.path ?? "/events/stream",
11
10
  requireAuth: auth,
@@ -16,6 +15,12 @@ const eventGatewayPluginImpl = async (fastify, opts = {}) => {
16
15
  });
17
16
  }
18
17
  if (opts.ws !== false) {
18
+ if (!fastify.hasDecorator("websocketServer")) try {
19
+ const wsPlugin = await import("@fastify/websocket");
20
+ await fastify.register(wsPlugin.default ?? wsPlugin);
21
+ } catch {
22
+ throw new Error("[arc-event-gateway] WebSocket support requires @fastify/websocket.\nInstall it: npm install @fastify/websocket\nOr disable WebSocket: eventGateway({ ws: false })");
23
+ }
19
24
  const { websocketPlugin } = await import("./websocket.mjs");
20
25
  await fastify.register(websocketPlugin, {
21
26
  path: opts.ws?.path ?? "/ws",
@@ -38,6 +43,5 @@ const eventGatewayPlugin = fp(eventGatewayPluginImpl, {
38
43
  name: "arc-event-gateway",
39
44
  fastify: "5.x"
40
45
  });
41
-
42
46
  //#endregion
43
- export { eventGatewayPlugin as default, eventGatewayPlugin };
47
+ export { eventGatewayPlugin as default, eventGatewayPlugin };
@@ -1,5 +1,7 @@
1
1
  import { WebSocketClient, WebSocketMessage, WebSocketPluginOptions } from "./websocket.mjs";
2
2
  import { EventGatewayOptions } from "./event-gateway.mjs";
3
- import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
4
3
  import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
5
- export { type EventGatewayOptions, type JobDefinition, type JobDispatchOptions, type JobDispatcher, type JobMeta, type JobsPluginOptions, type QueueStats, type StreamlinePluginOptions, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, type WorkflowLike, type WorkflowRunLike };
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-BJmgxNbF.mjs";
5
+ import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
6
+ import { WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription } from "./webhooks.mjs";
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 +1 @@
1
- export { };
1
+ export {};
@@ -10,7 +10,7 @@ interface JobDefinition<TData = unknown, TResult = unknown> {
10
10
  retries?: number;
11
11
  /** Backoff strategy */
12
12
  backoff?: {
13
- type: 'exponential' | 'fixed';
13
+ type: "exponential" | "fixed";
14
14
  delay: number;
15
15
  };
16
16
  /** Job timeout in ms (default: 30000) */
@@ -60,7 +60,7 @@ interface JobsPluginOptions {
60
60
  defaults?: {
61
61
  retries?: number;
62
62
  backoff?: {
63
- type: 'exponential' | 'fixed';
63
+ type: "exponential" | "fixed";
64
64
  delay: number;
65
65
  };
66
66
  timeout?: number;
@@ -27,23 +27,49 @@ const jobsPluginImpl = async (fastify, options) => {
27
27
  throw new Error("@classytic/arc/integrations/jobs requires \"bullmq\" package.\nInstall it: npm install bullmq");
28
28
  }
29
29
  const queues = /* @__PURE__ */ new Map();
30
+ const dlqQueues = /* @__PURE__ */ new Map();
30
31
  const workers = /* @__PURE__ */ new Map();
31
32
  for (const job of jobs) {
32
33
  const queueName = job.name;
33
34
  const queue = new Queue(queueName, { connection });
34
35
  queues.set(queueName, queue);
36
+ let dlqQueue = null;
37
+ if (job.deadLetterQueue != null) {
38
+ const dlqName = job.deadLetterQueue || `${queueName}:dead`;
39
+ dlqQueue = new Queue(dlqName, { connection });
40
+ dlqQueues.set(dlqName, dlqQueue);
41
+ }
42
+ const jobTimeout = job.timeout ?? defaults.timeout;
35
43
  const worker = new Worker(queueName, async (bullJob) => {
36
44
  const meta = {
37
45
  jobId: bullJob.id,
38
46
  attemptsMade: bullJob.attemptsMade,
39
47
  timestamp: Date.now()
40
48
  };
41
- const result = await job.handler(bullJob.data, meta);
42
- if (bridgeEvents && fastify.events?.publish) await fastify.events.publish(`job.${queueName}.completed`, {
43
- jobId: bullJob.id,
44
- data: bullJob.data,
45
- result
46
- });
49
+ let result;
50
+ if (jobTimeout) {
51
+ let timer;
52
+ const timeoutPromise = new Promise((_, reject) => {
53
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Job '${queueName}' timed out after ${jobTimeout}ms`)), jobTimeout);
54
+ });
55
+ try {
56
+ result = await Promise.race([job.handler(bullJob.data, meta), timeoutPromise]);
57
+ } finally {
58
+ clearTimeout(timer);
59
+ }
60
+ } else result = await job.handler(bullJob.data, meta);
61
+ if (bridgeEvents && fastify.events?.publish) try {
62
+ await fastify.events.publish(`job.${queueName}.completed`, {
63
+ jobId: bullJob.id,
64
+ data: bullJob.data,
65
+ result
66
+ });
67
+ } catch (err) {
68
+ fastify.log.warn({
69
+ err,
70
+ jobId: bullJob.id
71
+ }, `Failed to publish job.${queueName}.completed event`);
72
+ }
47
73
  return result;
48
74
  }, {
49
75
  connection,
@@ -54,12 +80,35 @@ const jobsPluginImpl = async (fastify, options) => {
54
80
  } : void 0
55
81
  });
56
82
  worker.on("failed", async (bullJob, error) => {
57
- if (bridgeEvents && fastify.events?.publish) await fastify.events.publish(`job.${queueName}.failed`, {
58
- jobId: bullJob?.id,
59
- data: bullJob?.data,
60
- error: error.message,
61
- attemptsMade: bullJob?.attemptsMade
62
- });
83
+ const maxAttempts = job.retries ?? defaults.retries ?? 3;
84
+ if (dlqQueue && bullJob && bullJob.attemptsMade >= maxAttempts) try {
85
+ await dlqQueue.add(`${queueName}:dead`, bullJob.data, {
86
+ jobId: `${bullJob.id}:dlq`,
87
+ removeOnComplete: false
88
+ });
89
+ fastify.log.warn({
90
+ jobId: bullJob.id,
91
+ dlq: job.deadLetterQueue ?? `${queueName}:dead`
92
+ }, `Job moved to dead-letter queue`);
93
+ } catch (dlqErr) {
94
+ fastify.log.error({
95
+ err: dlqErr,
96
+ jobId: bullJob.id
97
+ }, `Failed to move job to DLQ`);
98
+ }
99
+ if (bridgeEvents && fastify.events?.publish) try {
100
+ await fastify.events.publish(`job.${queueName}.failed`, {
101
+ jobId: bullJob?.id,
102
+ data: bullJob?.data,
103
+ error: error.message,
104
+ attemptsMade: bullJob?.attemptsMade
105
+ });
106
+ } catch (err) {
107
+ fastify.log.warn({
108
+ err,
109
+ jobId: bullJob?.id
110
+ }, `Failed to publish job.${queueName}.failed event`);
111
+ }
63
112
  });
64
113
  workers.set(queueName, worker);
65
114
  }
@@ -102,6 +151,7 @@ const jobsPluginImpl = async (fastify, options) => {
102
151
  const closePromises = [];
103
152
  for (const worker of workers.values()) closePromises.push(worker.close());
104
153
  for (const queue of queues.values()) closePromises.push(queue.close());
154
+ for (const dlq of dlqQueues.values()) closePromises.push(dlq.close());
105
155
  await Promise.all(closePromises);
106
156
  }
107
157
  };
@@ -118,6 +168,5 @@ const jobsPluginImpl = async (fastify, options) => {
118
168
  };
119
169
  /** Pluggable BullMQ job queue integration for Arc */
120
170
  const jobsPlugin = jobsPluginImpl;
121
-
122
171
  //#endregion
123
- export { jobsPlugin as default, jobsPlugin, defineJob };
172
+ export { jobsPlugin as default, jobsPlugin, defineJob };
@@ -0,0 +1,219 @@
1
+ import { Lt as ResourceDefinition } from "../../interface-DGmPxakH.mjs";
2
+ import { a as McpAuthResolver, c as McpResourceConfig, d as SessionEntry, 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, u as PromptResult } from "../../types-BJmgxNbF.mjs";
3
+ import { FastifyPluginAsync } from "fastify";
4
+ import { z } from "zod";
5
+
6
+ //#region src/integrations/mcp/createMcpServer.d.ts
7
+ /**
8
+ * Mutable auth ref — updated per-request by mcpPlugin.
9
+ * Tool handlers read from this to get the current request's auth context.
10
+ */
11
+ interface AuthRef {
12
+ current: McpAuthResult | null;
13
+ }
14
+ /**
15
+ * Create a configured MCP server from declarative config.
16
+ *
17
+ * @param config - Server name, version, tools, prompts
18
+ * @returns McpServer instance (not yet connected to a transport)
19
+ */
20
+ declare function createMcpServer(config: CreateMcpServerConfig, authRef?: AuthRef): Promise<McpServerInstance>;
21
+ /** Minimal interface for McpServer — avoids hard SDK type dependency */
22
+ interface McpServerInstance {
23
+ connect: (transport: unknown) => Promise<void>;
24
+ registerTool: (name: string, config: Record<string, unknown>, handler: (input: Record<string, unknown>, extra: Record<string, unknown>) => unknown) => unknown;
25
+ registerPrompt: (name: string, config: Record<string, unknown>, handler: (args: Record<string, unknown>) => unknown) => void;
26
+ resource: (...args: unknown[]) => void;
27
+ }
28
+ //#endregion
29
+ //#region src/integrations/mcp/definePrompt.d.ts
30
+ /** definePrompt() config */
31
+ interface DefinePromptConfig<TArgs extends Record<string, z.ZodTypeAny>> {
32
+ description: string;
33
+ title?: string;
34
+ /** Flat Zod shape for prompt arguments */
35
+ args?: TArgs;
36
+ handler: (args: { [K in keyof TArgs]: z.infer<TArgs[K]> }) => PromptResult;
37
+ }
38
+ /**
39
+ * Define a type-safe MCP prompt.
40
+ *
41
+ * @param name - Prompt name (snake_case recommended)
42
+ * @param config - Description, args schema, handler
43
+ */
44
+ declare function definePrompt<TArgs extends Record<string, z.ZodTypeAny>>(name: string, config: DefinePromptConfig<TArgs>): PromptDefinition;
45
+ //#endregion
46
+ //#region src/integrations/mcp/defineTool.d.ts
47
+ /** defineTool() config — uses flat Zod shapes for SDK compatibility */
48
+ interface DefineToolConfig<TInput extends Record<string, z.ZodTypeAny>> {
49
+ description: string;
50
+ title?: string;
51
+ /** Flat Zod shape: `{ name: z.string(), age: z.number() }` */
52
+ input?: TInput;
53
+ /** Flat Zod shape for structured output */
54
+ output?: Record<string, z.ZodTypeAny>;
55
+ annotations?: ToolAnnotations;
56
+ handler: (input: { [K in keyof TInput]: z.infer<TInput[K]> }, ctx: ToolContext) => Promise<CallToolResult>;
57
+ }
58
+ /**
59
+ * Define a type-safe MCP tool.
60
+ *
61
+ * @param name - Tool name (snake_case recommended)
62
+ * @param config - Tool description, input schema, annotations, handler
63
+ */
64
+ declare function defineTool<TInput extends Record<string, z.ZodTypeAny>>(name: string, config: DefineToolConfig<TInput>): ToolDefinition;
65
+ //#endregion
66
+ //#region src/integrations/mcp/fieldRulesToZod.d.ts
67
+ interface FieldRulesToZodOptions {
68
+ /** create: required enforced. update: all optional. list: filters + pagination. */
69
+ mode?: "create" | "update" | "list";
70
+ /** Fields hidden from all schemas */
71
+ hiddenFields?: string[];
72
+ /** Fields excluded from create/update schemas */
73
+ readonlyFields?: string[];
74
+ /** Extra fields to hide (e.g., from McpResourceConfig.hideFields) */
75
+ extraHideFields?: string[];
76
+ /** Filterable fields — only used in list mode */
77
+ filterableFields?: readonly string[];
78
+ }
79
+ /** Single field rule entry from Arc's schemaOptions.fieldRules */
80
+ interface FieldRuleEntry {
81
+ type?: string;
82
+ required?: boolean;
83
+ systemManaged?: boolean;
84
+ hidden?: boolean;
85
+ immutable?: boolean;
86
+ immutableAfterCreate?: boolean;
87
+ optional?: boolean;
88
+ minLength?: number;
89
+ maxLength?: number;
90
+ min?: number;
91
+ max?: number;
92
+ enum?: string[];
93
+ pattern?: string;
94
+ description?: string;
95
+ [key: string]: unknown;
96
+ }
97
+ /**
98
+ * Convert Arc fieldRules to a flat Zod shape.
99
+ *
100
+ * @returns Flat shape `Record<string, z.ZodTypeAny>` — pass directly to defineTool() or registerTool()
101
+ */
102
+ declare function fieldRulesToZod(fieldRules: Record<string, FieldRuleEntry> | undefined, options?: FieldRulesToZodOptions): Record<string, z.ZodTypeAny>;
103
+ //#endregion
104
+ //#region src/integrations/mcp/guards.d.ts
105
+ /** Check if the tool context has an authenticated user (not anonymous/null) */
106
+ declare function isAuthenticated(ctx: ToolContext): boolean;
107
+ /** Check if the tool context has an organization scope */
108
+ declare function hasOrg(ctx: ToolContext): boolean;
109
+ /** Check if the tool context matches a specific org */
110
+ declare function isOrg(ctx: ToolContext, orgId: string): boolean;
111
+ /** Get the current user ID from context (undefined if anonymous/null) */
112
+ declare function getUserId(ctx: ToolContext): string | undefined;
113
+ /** Get the current org ID from context */
114
+ declare function getOrgId(ctx: ToolContext): string | undefined;
115
+ /** Create a denied (isError) CallToolResult */
116
+ declare function denied(reason: string): CallToolResult;
117
+ /** Guard type — checks ToolContext, returns error message or null (pass) */
118
+ type McpGuard = (ctx: ToolContext) => string | null | Promise<string | null>;
119
+ /**
120
+ * Wrap a tool handler with one or more guards.
121
+ * If any guard returns a string, the tool returns an error with that message.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * defineTool('delete_all', {
126
+ * description: 'Delete everything',
127
+ * handler: guard(requireAuth, requireOrg, async (input, ctx) => {
128
+ * // only runs if authenticated + has org
129
+ * }),
130
+ * });
131
+ * ```
132
+ */
133
+ declare function guard(...args: [...McpGuard[], (input: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>]): (input: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;
134
+ /** Require authenticated user (not anonymous) */
135
+ declare const requireAuth: McpGuard;
136
+ /** Require organization context */
137
+ declare const requireOrg: McpGuard;
138
+ /** Require specific user role (checks session metadata if available) */
139
+ declare function requireRole(...roles: string[]): McpGuard;
140
+ /** Require specific organization */
141
+ declare function requireOrgId(orgId: string): McpGuard;
142
+ /**
143
+ * Custom guard from a predicate function.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const businessHoursOnly = customGuard(
148
+ * (ctx) => new Date().getHours() >= 9 && new Date().getHours() < 17,
149
+ * 'This tool is only available during business hours (9-5)',
150
+ * );
151
+ * ```
152
+ */
153
+ declare function customGuard(check: (ctx: ToolContext) => boolean | Promise<boolean>, errorMessage: string): McpGuard;
154
+ //#endregion
155
+ //#region src/integrations/mcp/sessionCache.d.ts
156
+ declare class McpSessionCache {
157
+ private sessions;
158
+ private ttlMs;
159
+ private maxSessions;
160
+ private cleanupTimer;
161
+ constructor(opts?: {
162
+ ttlMs?: number;
163
+ maxSessions?: number;
164
+ });
165
+ /** Get an existing session by ID */
166
+ get(sessionId: string): SessionEntry | undefined;
167
+ /** Store a new session */
168
+ set(sessionId: string, entry: SessionEntry): void;
169
+ /** Refresh the TTL on a session */
170
+ touch(sessionId: string): void;
171
+ /** Remove and close a session */
172
+ remove(sessionId: string): void;
173
+ /** Remove all expired sessions */
174
+ cleanup(): void;
175
+ /** Close all sessions and stop cleanup timer */
176
+ close(): void;
177
+ /** Current session count */
178
+ get size(): number;
179
+ /** Evict the oldest (least recently accessed) session */
180
+ private evictOldest;
181
+ /** Safely close a transport */
182
+ private closeTransport;
183
+ }
184
+ //#endregion
185
+ //#region src/integrations/mcp/mcpPlugin.d.ts
186
+ declare module "fastify" {
187
+ interface FastifyInstance {
188
+ mcp?: {
189
+ sessions: McpSessionCache | null;
190
+ toolNames: string[];
191
+ resourceNames: string[];
192
+ stateful: boolean;
193
+ };
194
+ }
195
+ }
196
+ declare const mcpPlugin: FastifyPluginAsync<McpPluginOptions>;
197
+ //#endregion
198
+ //#region src/integrations/mcp/resourceToTools.d.ts
199
+ interface ResourceToToolsConfig extends McpResourceConfig {
200
+ toolNamePrefix?: string;
201
+ /** Per-operation tool name overrides: `{ get: 'get_job_by_id' }` */
202
+ names?: Partial<Record<CrudOperation, string>>;
203
+ }
204
+ /**
205
+ * Convert a ResourceDefinition into MCP ToolDefinitions.
206
+ *
207
+ * MCP tools call BaseController directly — they bypass HTTP routes entirely.
208
+ * Therefore `disableDefaultRoutes` does NOT affect MCP tool generation;
209
+ * only `disabledRoutes` (the per-operation array) controls which ops are skipped.
210
+ *
211
+ * If the resource has an adapter but no controller (e.g. `disableDefaultRoutes: true`),
212
+ * a lightweight BaseController is auto-created from the adapter for MCP use.
213
+ *
214
+ * @param resource - Arc resource definition
215
+ * @param config - Optional overrides (operations, descriptions, hideFields, prefix, names)
216
+ */
217
+ declare function resourceToTools(resource: ResourceDefinition, config?: ResourceToToolsConfig): ToolDefinition[];
218
+ //#endregion
219
+ export { type AuthRef, type BetterAuthHandler, type CallToolResult, type CreateMcpServerConfig, type CrudOperation, type DefinePromptConfig, type DefineToolConfig, type FieldRuleEntry, type FieldRulesToZodOptions, type McpAuthResolver, type McpAuthResult, type McpGuard, type McpPluginOptions, type McpResourceConfig, type McpServerInstance, type PromptDefinition, type PromptResult, type ResourceToToolsConfig, type ToolAnnotations, type ToolContext, type ToolDefinition, createMcpServer, customGuard, definePrompt, defineTool, denied, fieldRulesToZod, getOrgId, getUserId, guard, hasOrg, isAuthenticated, isOrg, mcpPlugin, requireAuth, requireOrg, requireOrgId, requireRole, resourceToTools };