@classytic/arc 2.31.0 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +1 -1
  2. package/bin/arc.js +58 -17
  3. package/dist/{BaseController-De7GqHck.mjs → BaseController-CaQC5EXX.mjs} +298 -35
  4. package/dist/{EventTransport-2uVV34WU.d.mts → EventTransport-BEVP50ka.d.mts} +16 -0
  5. package/dist/{EventTransport-9mlTgqzE.mjs → EventTransport-tq4CEB-q.mjs} +39 -2
  6. package/dist/{HookSystem-BcmPyq5I.mjs → HookSystem-DqXkMXkB.mjs} +2 -2
  7. package/dist/{ResourceRegistry-qq0mn2s9.mjs → ResourceRegistry-DoALUlOO.mjs} +18 -15
  8. package/dist/{actionPermissions-BE5NwaQk.mjs → actionPermissions-CUeCmpUd.mjs} +1 -1
  9. package/dist/audit/index.d.mts +2 -2
  10. package/dist/audit/index.mjs +2 -2
  11. package/dist/auth/audit.mjs +1 -1
  12. package/dist/auth/index.d.mts +2 -2
  13. package/dist/auth/index.mjs +44 -11
  14. package/dist/auth/redis-session.mjs +1 -1
  15. package/dist/{authorizationDecision-ChvUQGG1.mjs → authorizationDecision-B3mP_QTy.mjs} +2 -2
  16. package/dist/{types-B7XeLEgK.d.mts → base-Csjc2oF-.d.mts} +74 -74
  17. package/dist/{buildHandler-CDrCJzmB.mjs → buildHandler-DLE9xX-V.mjs} +6 -5
  18. package/dist/cache/index.mjs +4 -4
  19. package/dist/{caching-BFHA6Scf.mjs → caching-BcGmvnHh.mjs} +2 -2
  20. package/dist/cleanup/index.d.mts +52 -18
  21. package/dist/cleanup/index.mjs +281 -32
  22. package/dist/cli/commands/describe.d.mts +1 -1
  23. package/dist/cli/commands/describe.mjs +4 -6
  24. package/dist/cli/commands/docs.mjs +3 -3
  25. package/dist/cli/commands/doctor.mjs +1 -1
  26. package/dist/cli/commands/generate.mjs +2 -2
  27. package/dist/cli/commands/init.mjs +2 -2
  28. package/dist/cli/commands/introspect.mjs +2 -2
  29. package/dist/cli/index.mjs +3 -3
  30. package/dist/{constants-D3Dv4LAV.mjs → constants-BxCoxmiN.mjs} +1 -1
  31. package/dist/context/index.mjs +3 -3
  32. package/dist/core/index.d.mts +2 -2
  33. package/dist/core/index.mjs +7 -7
  34. package/dist/{core-BGpIxINN.mjs → core-dxhcHhUD.mjs} +3 -3
  35. package/dist/{createActionRouter-CDXEdasl.mjs → createActionRouter-DVpoA0do.mjs} +14 -11
  36. package/dist/{createAggregationRouter-KrQDzx-k.mjs → createAggregationRouter-B_B48JZ-.mjs} +10 -8
  37. package/dist/{createApp-Df89BEni.mjs → createApp-CLGegqiG.mjs} +287 -62
  38. package/dist/{defineAggregation-DbPPS8RI.mjs → defineAggregation-Nv-DwfOf.mjs} +1 -1
  39. package/dist/{defineResource-Bv6EtN1x.mjs → defineResource-DR9vsUYy.mjs} +318 -35
  40. package/dist/discovery/index.mjs +1 -1
  41. package/dist/docs/index.d.mts +1 -1
  42. package/dist/docs/index.mjs +3 -3
  43. package/dist/{elevation-D2l1mh4g.mjs → elevation-B0tlY8y1.mjs} +4 -4
  44. package/dist/encryption/index.mjs +5 -4
  45. package/dist/{errorHandler-C5NeD6bT.mjs → errorHandler-C7xBqHsI.mjs} +42 -5
  46. package/dist/{errorHandler-CpYUJmPA.d.mts → errorHandler-DI5AN2b5.d.mts} +14 -3
  47. package/dist/{errors-Cj42AntO.mjs → errors-Cd_yy7aW.mjs} +1 -1
  48. package/dist/{eventPlugin-GF5nnBIq.d.mts → eventPlugin-DSNEZWfV.d.mts} +29 -1
  49. package/dist/{eventPlugin-CkffEihm.mjs → eventPlugin-Dar8qRaj.mjs} +68 -11
  50. package/dist/events/index.d.mts +18 -297
  51. package/dist/events/index.mjs +7 -758
  52. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  53. package/dist/events/transports/redis-stream-entry.mjs +14 -10
  54. package/dist/events/transports/redis.d.mts +1 -1
  55. package/dist/events/transports/redis.mjs +1 -1
  56. package/dist/factory/index.d.mts +20 -5
  57. package/dist/factory/index.mjs +131 -8
  58. package/dist/fastify-augmentation-DVsTAy6X.d.mts +179 -0
  59. package/dist/{fields-CB5Cuzk9.mjs → fields-CojeUkS4.mjs} +6 -11
  60. package/dist/{generate-EcA6ui4i.mjs → generate-DiB0nHPR.mjs} +2 -2
  61. package/dist/{headers-3zvE0rM3.mjs → headers-CveNO9gR.mjs} +2 -2
  62. package/dist/{health-DjZYjM7H.mjs → health-CasG2A28.mjs} +1 -1
  63. package/dist/hooks/index.d.mts +1 -1
  64. package/dist/hooks/index.mjs +2 -2
  65. package/dist/idempotency/index.d.mts +45 -1
  66. package/dist/idempotency/index.mjs +95 -11
  67. package/dist/idempotency/redis.mjs +1 -1
  68. package/dist/{index-CEJNZFtX.d.mts → index-B-DvUzsr.d.mts} +82 -3
  69. package/dist/{index-CpzxqePp.d.mts → index-BcR2bFZQ.d.mts} +707 -373
  70. package/dist/{index-Bc2ox8_U.d.mts → index-Brin0ODU.d.mts} +4 -4
  71. package/dist/{index-CFThn1K9.d.mts → index-C5NnMEKF.d.mts} +70 -4
  72. package/dist/{index-C-481Zf2.d.mts → index-ugr1tQlW.d.mts} +17 -1
  73. package/dist/index.d.mts +6 -6
  74. package/dist/index.mjs +11 -11
  75. package/dist/{init-BEiNZBsJ.mjs → init-mdJ-v4Xp.mjs} +24 -16
  76. package/dist/integrations/event-gateway.d.mts +1 -1
  77. package/dist/integrations/event-gateway.mjs +2 -2
  78. package/dist/integrations/index.d.mts +2 -2
  79. package/dist/integrations/index.mjs +1 -1
  80. package/dist/integrations/jobs/index.d.mts +1 -1
  81. package/dist/integrations/jobs/index.mjs +100 -98
  82. package/dist/integrations/mcp/index.d.mts +12 -3
  83. package/dist/integrations/mcp/index.mjs +2 -2
  84. package/dist/integrations/mcp/testing.d.mts +1 -1
  85. package/dist/integrations/mcp/testing.mjs +2 -2
  86. package/dist/integrations/streamline.mjs +3 -3
  87. package/dist/integrations/webhooks.d.mts +124 -1
  88. package/dist/integrations/webhooks.mjs +180 -2
  89. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  90. package/dist/integrations/websocket-redis.mjs +1 -1
  91. package/dist/integrations/websocket.mjs +38 -35
  92. package/dist/{interface-DYzM0q-u.mjs → interface-DznJrm9R.mjs} +1 -1
  93. package/dist/{jsonBody-BYbLDi5D.mjs → jsonBody-CfGH-GxO.mjs} +2 -2
  94. package/dist/{keys-C0_pa1tY.mjs → keys-WeKvY1dE.mjs} +1 -1
  95. package/dist/{loadResources-yawi9tAE.mjs → loadResources-Bj-aSsXi.mjs} +4 -3
  96. package/dist/{loadResourcesFromEntry-BAjYZonx.mjs → loadResourcesFromEntry-DwaxNcE2.mjs} +2 -2
  97. package/dist/logger/index.mjs +1 -1
  98. package/dist/{mcpPlugin-B_es0hiF.mjs → mcpPlugin-aJg5TqHi.mjs} +76 -36
  99. package/dist/{memory-DR6wpa9F.mjs → memory-qYSB7rMQ.mjs} +5 -5
  100. package/dist/{metrics-CUdw_wLB.mjs → metrics-D7uchLjN.mjs} +2 -2
  101. package/dist/middleware/index.d.mts +1 -1
  102. package/dist/middleware/index.mjs +3 -3
  103. package/dist/migrations/index.mjs +3 -3
  104. package/dist/{module-0pFJA6YW.mjs → module-CwKsgZXp.mjs} +1 -1
  105. package/dist/{multipartBody-wOgj_fnj.mjs → multipartBody-4w-S3-PU.mjs} +3 -3
  106. package/dist/{openapi-B2L4js1v.mjs → openapi-BN5cQaO2.mjs} +6 -6
  107. package/dist/outbox-admin/index.d.mts +85 -0
  108. package/dist/outbox-admin/index.mjs +115 -0
  109. package/dist/outbox-module-CXB79bvt.mjs +839 -0
  110. package/dist/outbox-module-D-VFq37T.d.mts +368 -0
  111. package/dist/permissions/index.d.mts +3 -3
  112. package/dist/permissions/index.mjs +6 -6
  113. package/dist/{permissions-L--QDAir.mjs → permissions-BAfwgBxc.mjs} +120 -8
  114. package/dist/{pipe-DRDNsI2T.mjs → pipe-BzzoEhHj.mjs} +3 -5
  115. package/dist/pipeline/index.d.mts +1 -1
  116. package/dist/pipeline/index.mjs +2 -2
  117. package/dist/{pipeline-UPQFatit.mjs → pipeline-q69DZzgG.mjs} +17 -15
  118. package/dist/plugins/index.d.mts +6 -6
  119. package/dist/plugins/index.mjs +16 -16
  120. package/dist/plugins/response-cache.mjs +2 -2
  121. package/dist/plugins/tracing-entry.mjs +2 -2
  122. package/dist/{pluralize-O0FZgESl.mjs → pluralize-B7xf8qZF.mjs} +1 -1
  123. package/dist/{preloadResources-Dz6BjzM6.mjs → preloadResources-BkyqeTDz.mjs} +1 -1
  124. package/dist/{preloadResources-CyoJMSiO.d.mts → preloadResources-g07ldtzh.d.mts} +30 -7
  125. package/dist/presets/filesUpload.d.mts +2 -2
  126. package/dist/presets/filesUpload.mjs +6 -6
  127. package/dist/presets/index.d.mts +2 -2
  128. package/dist/presets/index.mjs +2 -2
  129. package/dist/presets/multiTenant.d.mts +1 -1
  130. package/dist/presets/multiTenant.mjs +3 -3
  131. package/dist/presets/search.d.mts +2 -2
  132. package/dist/presets/search.mjs +2 -2
  133. package/dist/{presets-C4-g4_4P.mjs → presets-D-h0uWtP.mjs} +6 -5
  134. package/dist/projection-BKM0Ui-s.mjs +42 -0
  135. package/dist/{queryCachePlugin-BZr8v46E.mjs → queryCachePlugin-DhsZ0ZIc.mjs} +50 -8
  136. package/dist/{rateLimit-CMPY5Qms.mjs → rateLimit-D4-ASwsI.mjs} +2 -2
  137. package/dist/{rateLimitKey-Dn5JNmkj.mjs → rateLimitKey-XngqVNEV.mjs} +1 -1
  138. package/dist/{realtime-C5NoxWFY.mjs → realtime-CTkv3O59.mjs} +10 -10
  139. package/dist/{redis-stream-qN6A-RFv.d.mts → redis-stream-mwLWlfHG.d.mts} +1 -1
  140. package/dist/{registerRoute-CBN84fc7.mjs → registerRoute-DNnd3vkz.mjs} +10 -8
  141. package/dist/registry/index.d.mts +3 -3
  142. package/dist/registry/index.mjs +3 -3
  143. package/dist/{registry-4213U4zb.mjs → registry-CLxCR5LF.mjs} +2 -2
  144. package/dist/{renewingLease-Dll8mGgc.mjs → renewingLease-BnWtbA2U.mjs} +1 -1
  145. package/dist/{replyHelpers-DIiZKGUt.mjs → replyHelpers-BSBVCwL3.mjs} +2 -2
  146. package/dist/{repositoryFilter-f9eAfq_l.mjs → repositoryFilter-DKny7PDn.mjs} +1 -1
  147. package/dist/{requestContext-2TxXOJ7T.mjs → requestContext-uYE35JxI.mjs} +1 -1
  148. package/dist/{rolldown-runtime-CUsjNX9R.mjs → rolldown-runtime-DNg-KNCk.mjs} +1 -1
  149. package/dist/runtimeCapabilities-Dm88DaTW.mjs +49 -0
  150. package/dist/{schedules-DdtlICTf.mjs → schedules-CAcZgia0.mjs} +3 -3
  151. package/dist/{schemaConverter-D--wdJM-.mjs → schemaConverter-XH1RZRyu.mjs} +1 -1
  152. package/dist/{schemaIR-Bm2enbnh.mjs → schemaIR-CDvclAew.mjs} +2 -2
  153. package/dist/schemas/index.mjs +1 -1
  154. package/dist/scim/index.mjs +2 -2
  155. package/dist/scope/index.mjs +5 -5
  156. package/dist/{scope-DHx5Fous.mjs → scope-BaWcqBvr.mjs} +4 -4
  157. package/dist/{simpleEqualityMatcher-DjQJ7FKA.mjs → simpleEqualityMatcher-Se8VrsaP.mjs} +1 -1
  158. package/dist/{sse-CUTo9y0X.mjs → sse-Cf0jUHsh.mjs} +7 -6
  159. package/dist/{sseStream-B_5XcVVd.mjs → sseStream-Bvpi-UXQ.mjs} +2 -2
  160. package/dist/{store-helpers-D1MP_gRG.mjs → store-helpers-B88p99Z2.mjs} +1 -1
  161. package/dist/{streaming-PSZwi8JV.mjs → streaming-DsT1B5zJ.mjs} +1 -1
  162. package/dist/{subscribe-helpers-C0Tb2aUp.mjs → subscribe-helpers-CLIOHsOK.mjs} +2 -2
  163. package/dist/sync/changelog.mjs +1 -1
  164. package/dist/testing/cleanupStoreContract.d.mts +1 -1
  165. package/dist/testing/cleanupStoreContract.mjs +12 -6
  166. package/dist/testing/index.d.mts +52 -13
  167. package/dist/testing/index.mjs +110 -40
  168. package/dist/testing/outboxStoreContract.d.mts +11 -2
  169. package/dist/testing/outboxStoreContract.mjs +110 -21
  170. package/dist/testing/storageContract.mjs +11 -6
  171. package/dist/{transactionContext-dAfBVUrk.mjs → transactionContext-BYtWnwAS.mjs} +1 -1
  172. package/dist/{typeGuards-B0ryPo3I.mjs → typeGuards-jjKJDN13.mjs} +1 -1
  173. package/dist/types/index.d.mts +3 -3
  174. package/dist/types/index.mjs +1 -1
  175. package/dist/types/storage.mjs +1 -1
  176. package/dist/{types-CBgvkp8O.d.mts → types-BcTumAz_.d.mts} +128 -18
  177. package/dist/{types-CwcUQ610.d.mts → types-BfC83KsP.d.mts} +15 -1
  178. package/dist/{types-Cys0ko9Z.mjs → types-CoqIRrkY.mjs} +4 -4
  179. package/dist/{types-C4QE8qnm.d.mts → types-DVLddu2q.d.mts} +144 -1
  180. package/dist/usage/index.d.mts +1 -1
  181. package/dist/usage/index.mjs +0 -0
  182. package/dist/{userHelpers-ChYJ6CdK.mjs → userHelpers-CWF5pbZD.mjs} +1 -1
  183. package/dist/utils/index.d.mts +57 -5
  184. package/dist/utils/index.mjs +12 -11
  185. package/dist/{utils-vL8BrDE2.mjs → utils-h1V_sT-t.mjs} +38 -7
  186. package/dist/{validate-_KbifpHF.mjs → validate-CdQlz52F.mjs} +2 -2
  187. package/dist/{versioning-D_rhHfZm.d.mts → versioning-DEs-_hFi.d.mts} +1 -1
  188. package/dist/{versioning-CHDTor3P.mjs → versioning-rZbyLVD-.mjs} +4 -4
  189. package/package.json +31 -20
  190. package/skills/arc/SKILL.md +96 -4
  191. package/skills/arc/references/api-reference.md +19 -0
  192. package/skills/arc/references/production.md +2 -2
  193. package/dist/fastify-augmentation-zh0_AYHa.d.mts +0 -85
  194. package/dist/projection-BHw6xlOQ.mjs +0 -18
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { m as RESERVED_QUERY_PARAMS } from "./constants-D3Dv4LAV.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { m as RESERVED_QUERY_PARAMS } from "./constants-BxCoxmiN.mjs";
3
3
  import { arcLog } from "./logger/index.mjs";
4
- import { t as ArcError } from "./errors-Cj42AntO.mjs";
4
+ import { t as ArcError, u as ValidationError } from "./errors-Cd_yy7aW.mjs";
5
5
  import { errorContractSchema as errorContractSchema$1, errorDetailSchema as errorDetailSchema$1 } from "@classytic/repo-core/errors";
6
6
  import { scheduleBackground } from "@classytic/repo-core/cache";
7
7
  //#region src/utils/circuitBreaker.ts
@@ -591,6 +591,11 @@ const DANGEROUS_REGEX_PATTERNS = /(\{[0-9,]+\}|\*\+|\+\+|\?\+|(\(.+\))\+|\(\?:|\
591
591
  * For advanced MongoDB features ($lookup, aggregations), use MongoKit's QueryParser.
592
592
  */
593
593
  var ArcQueryParser = class {
594
+ /**
595
+ * PUBLIC, deliberately: `QueryResolver` reads it so it does not apply a second cap
596
+ * on top of this one. Three layers capping independently, lowest winning silently,
597
+ * is what made a 696-row chart of accounts render as 100.
598
+ */
594
599
  maxLimit;
595
600
  defaultLimit;
596
601
  maxRegexLength;
@@ -599,6 +604,7 @@ var ArcQueryParser = class {
599
604
  _allowedFilterFields;
600
605
  _allowedSortFields;
601
606
  _allowedOperators;
607
+ strictFilterFields;
602
608
  /** Allowed filter fields (used by MCP for auto-derive) */
603
609
  allowedFilterFields;
604
610
  /** Allowed sort fields (used by MCP for sort descriptions) */
@@ -621,6 +627,7 @@ var ArcQueryParser = class {
621
627
  this.maxRegexLength = options.maxRegexLength ?? 200;
622
628
  this.maxSearchLength = options.maxSearchLength ?? 200;
623
629
  this.maxFilterDepth = options.maxFilterDepth ?? 10;
630
+ this.strictFilterFields = options.strictFilterFields ?? process.env.ARC_STRICT_QUERY_PARAMS === "true";
624
631
  if (options.allowedFilterFields) {
625
632
  this._allowedFilterFields = new Set(options.allowedFilterFields);
626
633
  this.allowedFilterFields = options.allowedFilterFields;
@@ -787,7 +794,10 @@ var ArcQueryParser = class {
787
794
  if (value === void 0 || value === null) return;
788
795
  if (!/^[a-zA-Z_][a-zA-Z0-9_.]*(?:\[[a-z]+\])?$/.test(key)) return;
789
796
  const bareKey = key.replace(/\[[a-z]+\]$/, "");
790
- if (this._allowedFilterFields && !this._allowedFilterFields.has(bareKey)) return;
797
+ if (this._allowedFilterFields && !this._allowedFilterFields.has(bareKey)) {
798
+ if (this.strictFilterFields) throw new ValidationError(`Unknown filter field: ${bareKey}. Allowed: ${[...this._allowedFilterFields].sort().join(", ")}`);
799
+ return;
800
+ }
791
801
  if (this.exceedsDepth(value)) return;
792
802
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
793
803
  const operatorObj = value;
@@ -879,10 +889,31 @@ var ArcQueryParser = class {
879
889
  },
880
890
  limit: {
881
891
  type: "integer",
882
- description: "Number of items per page",
892
+ /**
893
+ * The cap is DOCUMENTED here, not enforced — deliberately.
894
+ *
895
+ * Both layers that handle `limit` CLAMP it: this parser
896
+ * (`Math.min(…, this.maxLimit)`) and `QueryResolver`. Emitting
897
+ * `maximum` contradicted them, because arc layers this schema as the
898
+ * route's `listQuery` and Fastify validates the querystring BEFORE
899
+ * either runs — so the clamp was unreachable and `?limit=200` became a
900
+ * hard 400.
901
+ *
902
+ * That combination is worse than either policy alone. Asking for more
903
+ * rows than allowed is a benign over-ask with an obvious right answer
904
+ * (give the maximum); turning it into a validation failure means a UI
905
+ * passing a generous page size gets NOTHING, and a caller that does not
906
+ * inspect the error body renders it as an empty list — a 400 disguised
907
+ * as "no results", which is the hardest kind to find.
908
+ *
909
+ * A deployment wanting refusal instead of clamping should say so in the
910
+ * parser's policy, where both halves can agree, not in a schema that
911
+ * silently outranks them. Mirrors the same fix in mongokit's
912
+ * `buildQuerySchema`.
913
+ */
914
+ description: `Number of items per page (values above ${this.maxLimit} are capped to ${this.maxLimit})`,
883
915
  default: this.defaultLimit,
884
- minimum: 1,
885
- maximum: this.maxLimit
916
+ minimum: 1
886
917
  },
887
918
  sort: {
888
919
  type: "string",
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
3
3
  //#region src/core/aggregation/normalize.ts
4
4
  /**
5
5
  * Framework cap on URL-driven aggregation limits when the host opts
@@ -1,5 +1,5 @@
1
1
 
2
- import { n as DomainEvent } from "./EventTransport-2uVV34WU.mjs";
2
+ import { n as DomainEvent } from "./EventTransport-BEVP50ka.mjs";
3
3
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
4
4
  //#region src/plugins/caching.d.ts
5
5
  interface CachingRule {
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
3
- import { d as createDomainError } from "./errors-Cj42AntO.mjs";
1
+ /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
3
+ import { d as createDomainError } from "./errors-Cd_yy7aW.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/plugins/versioning.ts
6
6
  var versioning_exports = /* @__PURE__ */ __exportAll({
@@ -31,7 +31,7 @@ const versioningPlugin = async (fastify, opts) => {
31
31
  reply.header(responseHeader, version);
32
32
  if (deprecatedSet.has(version)) {
33
33
  reply.header("deprecation", "true");
34
- reply.header("sunset", sunset ?? new Date(Date.now() + 2160 * 60 * 60 * 1e3).toISOString());
34
+ reply.header("sunset", sunset ?? new Date(Date.now() + 7776e6).toISOString());
35
35
  }
36
36
  });
37
37
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/arc",
3
- "version": "2.31.0",
3
+ "version": "2.33.0",
4
4
  "description": "Resource-oriented backend framework for Fastify - clean, minimal, powerful, tree-shakable",
5
5
  "type": "module",
6
6
  "exports": {
@@ -220,6 +220,10 @@
220
220
  "types": "./dist/cleanup/index.d.mts",
221
221
  "default": "./dist/cleanup/index.mjs"
222
222
  },
223
+ "./outbox-admin": {
224
+ "types": "./dist/outbox-admin/index.d.mts",
225
+ "default": "./dist/outbox-admin/index.mjs"
226
+ },
223
227
  "./testing/outbox": {
224
228
  "types": "./dist/testing/outboxStoreContract.d.mts",
225
229
  "default": "./dist/testing/outboxStoreContract.mjs"
@@ -290,7 +294,8 @@
290
294
  "./auth/audit": "stable",
291
295
  "./sync": "experimental",
292
296
  "./usage": "experimental",
293
- "./testing/outbox": "stable"
297
+ "./testing/outbox": "stable",
298
+ "./outbox-admin": "experimental"
294
299
  }
295
300
  },
296
301
  "files": [
@@ -331,21 +336,23 @@
331
336
  "test:smoke:redis": "node scripts/smoke-ws-redis.mjs",
332
337
  "smoke": "node scripts/smoke-test.mjs",
333
338
  "smoke:consumer": "node scripts/smoke-test.mjs --consumer",
339
+ "smoke:mcp": "node scripts/mcp-e2e.mjs",
334
340
  "push": "classytic-push",
335
341
  "release:tag": "node -e \"require('child_process').execSync('npm run push -- v'+require('./package.json').version,{stdio:'inherit'})\"",
336
342
  "release": "npm run push -- main && npm run release:tag && npm publish",
337
343
  "check:peer-skew": "node scripts/check-peer-skew.mjs",
338
344
  "check:docs-drift": "node scripts/check-docs-drift.mjs",
339
345
  "check:boundaries": "node scripts/check-boundaries.mjs",
340
- "prepublishOnly": "npm run check:peer-skew && npm run check:docs-drift && npm run check:route-api && npm run check:boundaries && npm run typecheck && npm run lint && npm run build && npm run check:api-surface && npm run check:package && npm run test:ci && npm run smoke:consumer",
341
- "check:route-api": "node scripts/check-removed-route-api.mjs"
346
+ "prepublishOnly": "npm run check:peer-skew && npm run check:docs-drift && npm run check:route-api && npm run check:boundaries && npm run typecheck && npm run lint && npm run build && npm run check:api-surface && npm run check:forgotten-exports && npm run check:package && npm run test:ci && npm run smoke:consumer && npm run smoke:mcp",
347
+ "check:route-api": "node scripts/check-removed-route-api.mjs",
348
+ "check:forgotten-exports": "node scripts/check-forgotten-exports.mjs"
342
349
  },
343
350
  "engines": {
344
351
  "node": ">=22"
345
352
  },
346
353
  "peerDependencies": {
347
- "@classytic/primitives": ">=0.18.0",
348
- "@classytic/repo-core": ">=0.17.0",
354
+ "@classytic/primitives": ">=0.23.0",
355
+ "@classytic/repo-core": ">=0.24.0",
349
356
  "@classytic/streamline": ">=2.8.0",
350
357
  "@fastify/cors": ">=11.0.0",
351
358
  "@fastify/helmet": ">=13.0.0",
@@ -353,6 +360,7 @@
353
360
  "@fastify/multipart": ">=9.0.0",
354
361
  "@fastify/rate-limit": ">=10.0.0",
355
362
  "@fastify/sensible": ">=6.0.0",
363
+ "@fastify/static": ">=8.0.0",
356
364
  "@fastify/type-provider-typebox": ">=6.0.0",
357
365
  "@fastify/under-pressure": ">=9.0.0",
358
366
  "@fastify/websocket": ">=11.0.0",
@@ -372,8 +380,8 @@
372
380
  "jose": ">=5.0.0",
373
381
  "pino-pretty": ">=13.0.0",
374
382
  "typebox": ">=1.0.0",
375
- "zod": ">=4.4.0",
376
- "@fastify/static": ">=8.0.0"
383
+ "vitest": ">=3.0.0",
384
+ "zod": ">=4.4.0"
377
385
  },
378
386
  "peerDependenciesMeta": {
379
387
  "better-auth": {
@@ -456,6 +464,9 @@
456
464
  },
457
465
  "@fastify/static": {
458
466
  "optional": true
467
+ },
468
+ "vitest": {
469
+ "optional": true
459
470
  }
460
471
  },
461
472
  "dependencies": {
@@ -467,14 +478,14 @@
467
478
  "@arethetypeswrong/cli": "^0.18.5",
468
479
  "@better-auth/drizzle-adapter": "^1.6.19",
469
480
  "@better-auth/mongo-adapter": "^1.6.19",
470
- "@biomejs/biome": "^2.5.5",
481
+ "@biomejs/biome": "^2.5.7",
471
482
  "@classytic/dev-tools": "^0.2.0",
472
483
  "@classytic/gitkit": ">=0.1.0",
473
- "@classytic/mongokit": "^3.28.0",
484
+ "@classytic/mongokit": "^3.33.0",
474
485
  "@classytic/pgkit": "^0.1.0",
475
- "@classytic/primitives": "^0.18.0",
476
- "@classytic/repo-core": "^0.17.0",
477
- "@classytic/sqlitekit": "^0.6.0",
486
+ "@classytic/primitives": "^0.23.0",
487
+ "@classytic/repo-core": "^0.24.0",
488
+ "@classytic/sqlitekit": "^0.9.0",
478
489
  "@classytic/streamline": "^2.8.2",
479
490
  "@electric-sql/pglite": "^0.5.4",
480
491
  "@fastify/cors": "^11.2.0",
@@ -494,21 +505,21 @@
494
505
  "@vitest/coverage-v8": "^4.1.9",
495
506
  "ajv": "^8.20.0",
496
507
  "better-auth": "^1.6.25",
497
- "better-sqlite3": "^12.11.1",
498
- "bullmq": "^5.81.2",
508
+ "better-sqlite3": "^13.0.3",
509
+ "bullmq": "^6.1.2",
499
510
  "dotenv": "^17.4.2",
500
511
  "drizzle-orm": "^0.45.2",
501
512
  "fast-check": "^4.8.0",
502
- "fastify": "^5.10.0",
503
- "fastify-raw-body": "^5.0.0",
513
+ "fastify": "^5.12.0",
514
+ "fastify-raw-body": "^6.0.1",
504
515
  "h3-js": "^4.4.0",
505
- "ioredis": "^5.11.1",
516
+ "ioredis": "^6.0.0",
506
517
  "jose": "^6.2.3",
507
518
  "jsonwebtoken": "^9.0.3",
508
- "knip": "^6.17.1",
519
+ "knip": "^6.31.0",
509
520
  "mongodb": "^7.3.0",
510
521
  "mongodb-memory-server": "^11.2.0",
511
- "mongoose": "^9.7.1",
522
+ "mongoose": "^9.9.1",
512
523
  "publint": "^0.3.21",
513
524
  "tsdown": "^0.22.14",
514
525
  "typebox": "^1.3.6",
@@ -69,6 +69,14 @@ await app.listen({ port: 8040, host: '0.0.0.0' });
69
69
 
70
70
  **Boot order (fixed):** `beforeBoot` → modules resolve → `plugins` → `bootstrap[]` → `resources` → `afterResources` → `onReady`.
71
71
 
72
+ **`role` — one image, N deployments (2.34).** `createApp({ role })` declares what a process is FOR, so the same codebase ships as api / worker / relay / scheduler without per-deployment builds:
73
+
74
+ ```typescript
75
+ createApp({ role: process.env.ARC_ROLE as 'api' | 'worker' | 'relay' | 'scheduler' | 'all' })
76
+ ```
77
+
78
+ `api` and `worker` mount NO module schedule arms; `relay` mounts only `kind: 'relay'` arms (the outbox relay, durable webhook delivery); `scheduler` mounts all of them. Declaring `relay`/`scheduler` together with `resources`/`resourceDir` is BOOT-FATAL — a relay process must not serve routes. Default `all` is the pre-2.34 behaviour, unchanged. `createWorker()` forwards `role`; without one it still runs everything non-HTTP.
79
+
72
80
  `beforeBoot()` (2.21) runs before Fastify exists and before module thunks / resource files import — connect the DB there instead of dynamic-import ordering hacks.
73
81
 
74
82
  For async-booted engines, pass `resources` as a factory so it runs after `bootstrap[]`:
@@ -162,7 +170,7 @@ defineResource({ name: 'health', customRoutesOnly: true, routes: [...] });
162
170
  defineResource({ name: 'pipeline', adapter, defaultLimit: 200, maxLimit: 500 });
163
171
  ```
164
172
 
165
- 400s on `?limit=` violations include the cap: `message: "Query parameter 'limit' must be <= 500 (got 800) (cap is 500)"` + `meta: { field: 'limit', cap: 500 }`.
173
+ A `?limit=` above the cap is **clamped to it**, not rejected (2.33 — see Query parsing). The effective cap rides in the OpenAPI parameter description.
166
174
 
167
175
  ## fieldRules
168
176
 
@@ -304,12 +312,18 @@ Decorates `app.authenticate` / `app.optionalAuthenticate` / `app.authorize`.
304
312
  ## Authoritative gotchas
305
313
 
306
314
  - **Field-write reject (default).** Requests carrying non-writable fields → 403 with denied list. Opt into silent strip: `defineResource({ onFieldWriteDenied: 'strip' })`.
315
+ - **Immutable writes are STRIPPED, not refused (default).** `PATCH {"type":"rent"}` on an `immutable` field returns 200 with the value unchanged. Opt into 403: `defineResource({ onImmutableWrite: 'reject' })`, or `ARC_STRICT_IMMUTABLE_WRITES=true`. Separate from `onFieldWriteDenied` because a full-object PATCH echoing an immutable field back UNCHANGED is legitimate, and the sanitizer has no stored document to tell that from a real change — turn it on if you send partial patches.
316
+ - **Log redaction is REPLACED, not merged.** Setting `logger.redact` makes yours the whole policy; arc's defaults stop applying. Superset them: `import { DEFAULT_LOGGER_REDACT_PATHS } from '@classytic/arc/factory'` and spread it into your list, rather than hand-copying (a copy drifts — one host's had lost `*.passwordHash`).
317
+ - **Error disclosure derives from `preset`, falling back to the environment.** `includeStack` and `exposeInternalMessages` are both derived, and host keys MERGE over them — stating one no longer resets the other. Direct `errorHandlerPlugin` registration skips the preset derivation entirely.
307
318
  - **multiTenant injects org on UPDATE.** Body `organizationId` is overwritten with caller's scope (closes tenant-hop).
308
319
  - **`request.user`** is `Record<string, unknown> | undefined`. Guard with `if (req.user)` on public routes.
309
320
  - **Arc reads singular `user.role`** (string, comma-separated, or array). Don't put a plural `roles` field on the model.
310
321
  - **`verifySignature(body, …)`** throws on parsed body — pass `req.rawBody`.
311
322
  - **MCP tools fail-closed.** A resource action without per-action perm + no `actionPermissions` + no `permissions.update` fallback → throws at tool generation. Declare `allowPublic()` to opt into unauthenticated.
312
323
  - **Better Auth roles.** BA stores `member.role = "admin,recruiter"` (comma-separated). Arc splits into `scope.orgRoles = ['admin', 'recruiter']`; `?role=admin` won't match — use `role[like]=admin`.
324
+ - **Read-only repositories refuse write ROUTES at boot (2.34).** Kits' Better Auth overlays are sealed by default (`capabilities.readOnly`) — BA owns hashing, session revocation, org cascades. Mounting `create`/`update`/`delete` over one is boot-fatal: disable those routes, or pass the kit's `unsafeWritable: true` for administrative repair only. Normal identity mutations go through `auth.api`.
325
+ - **Arc closes only stores/transports it BUILT (2.34).** One you hand to `idempotencyPlugin` / `eventPlugin` / `queryCachePlugin` stays yours to close — arc used to close every one, which wiped a co-resident app's live idempotency records.
326
+ - **Idempotency self-checks its store at boot (2.34).** A probe write must read back under its own key and NOT under another; registration throws otherwise. Catches a filter-stripping repository (a pathless Mongoose schema under `strictQuery: true` strips filters to `{}`), which otherwise replays cached responses across keys AND users. `selfCheck: false` to disable; the adapter's per-read identity checks remain.
313
327
 
314
328
  ## routeGuards + defineGuard
315
329
 
@@ -369,6 +383,28 @@ defineResource({
369
383
  });
370
384
  ```
371
385
 
386
+ **An unknown filter key is DROPPED by default, and dropping WIDENS.** `?filters[status]=pending`
387
+ against a parser that does not whitelist `status` returns 200 with every row unfiltered — the
388
+ caller asked to narrow and got a broader set. Refuse instead:
389
+
390
+ ```typescript
391
+ new QueryParser({ allowedFilterFields: [...], strictFilterFields: true }) // 400 ValidationError
392
+ ```
393
+
394
+ Off by default (an unconditional throw would reject requests that work today). Fleet-wide via
395
+ `ARC_STRICT_QUERY_PARAMS=true`; an explicit `false` beats the env. Only the FILTER whitelist —
396
+ a dropped sort or operator degrades ordering, it does not widen the row set.
397
+
398
+ **`?limit` above the cap CLAMPS, it does not 400** (2.33). The parser and `QueryResolver` both
399
+ clamp, and the querystring schema no longer emits `maximum` — it used to reject first, making the
400
+ documented clamp unreachable and turning a benign over-ask into a 400 that callers render as an
401
+ empty list. The cap is in the parameter description instead, so OpenAPI still shows it.
402
+
403
+ **One cap, one owner.** Page size is capped by the parser, the repository's pagination engine and
404
+ arc — lowest wins silently. Arc defers to the parser's `maxLimit` when the host states none, and
405
+ WARNS at boot when the parser allows more than the repository (`pagination-cap-mismatch`).
406
+ Configure both to the same number.
407
+
372
408
  ## Aggregations
373
409
 
374
410
  `GET /:prefix/aggregations/:name` per entry. Portable `$match → $group → $project → $sort → $limit` against `repo.aggregate(req, options)` — same shape across kits.
@@ -453,6 +489,31 @@ defineResource({
453
489
 
454
490
  POST/PATCH/DELETE bumps resource version. Response header: `x-cache: HIT | STALE | MISS`. `runtime: 'distributed'` requires `stores.queryCache: RedisCacheStore`.
455
491
 
492
+ ## Transactional writes (2.34)
493
+
494
+ `transactional: true` runs the PERSISTENCE step inside `withTransaction`. Write verbs receive the tx-bound repository plus `ctx.uow` — the join point for work outside the repository:
495
+
496
+ ```typescript
497
+ defineResource({
498
+ name: 'order',
499
+ adapter,
500
+ transactional: true,
501
+ writes: {
502
+ create: async (data, ctx) => {
503
+ const order = await ctx.repository.create(data); // tx-bound
504
+ await outbox.store(event, { session: ctx.uow?.session }); // SAME transaction
505
+ return order;
506
+ },
507
+ },
508
+ });
509
+ ```
510
+
511
+ - Hooks stay OUTSIDE the retry — before/after run once; only persistence re-runs.
512
+ - Transient conflicts re-run with jittered backoff; `VersionConflictError` surfaces (409), never retried.
513
+ - **Retry authority is the KIT's.** mongokit declares `transactionRetry: 'managed'` (its driver already retries internally) and is called exactly once; sqlitekit declares `'caller'` and gets the envelope's loop. Arc never stacks a second policy.
514
+ - **BOOT-FATAL** without `withTransaction`, and at REGISTRATION when the repository reports `capabilities.transactions !== true` (standalone Mongo, D1, unconfirmed topology). The capability is checked at registration, not at `defineResource()`, because that usually runs at module import — before `beforeBoot()` connects.
515
+ - `ctx.uow.session` is empty on connection-bound backends (SQLite); the tx-bound repo is the join point there.
516
+
456
517
  ## Events
457
518
 
458
519
  CRUD events auto-emit: `{resource}.created` / `{resource}.updated` / `{resource}.deleted`. Manual publish:
@@ -464,13 +525,27 @@ await app.events.subscribe('order.*', async (event) => { … });
464
525
 
465
526
  Transports: Memory · Redis Pub/Sub (fire-and-forget) · Redis Streams (durable, consumer groups, DLQ). Event types live in `@classytic/primitives/events` (`createEvent`, `createChildEvent`, `matchEventPattern`, …); arc re-exports the runtime `MemoryEventTransport` only.
466
527
 
467
- **Outbox** (at-least-once via transactional outbox):
528
+ **Outbox** (at-least-once via transactional outbox) — prefer the MODULE, so the store is a slot other modules depend on rather than a value threaded by hand:
529
+
530
+ ```typescript
531
+ createApp({ modules: [createOutboxModule({ store: repositoryAsOutboxStore(outboxRepo) })] });
532
+ // Dev: createOutboxModule({ store: new MemoryOutboxStore() })
533
+ ```
534
+
535
+ The relay arm is `kind: 'relay'`, so `role: 'relay'` deployments drain it. It resolves the RAW transport, never the `fastify.events` facade (the facade fails open, which would acknowledge rows whose publish failed) — no transport is boot-fatal. **Fencing (2.34):** stores that support `claimPendingFenced` mint a token in the claim CAS; a relay that lost its lease mid-batch cannot ack or re-schedule a row a successor already owns. Feature-detected end to end.
536
+
537
+ **Durable webhooks (2.34)** — delivery survives crash and deploy, using the outbox machinery rather than a second queue:
468
538
 
469
539
  ```typescript
470
- const outbox = new EventOutbox({ repository: outboxRepo, transport });
471
- // Dev: { store: new MemoryOutboxStore(), transport }
540
+ const store = repositoryAsOutboxStore(deliveryRepo);
541
+ createApp({
542
+ modules: [createDurableWebhookModule({ store })], // relay does the signed POST
543
+ plugins: async (f) => f.register(webhookPlugin, { durable: { store } }), // enqueue, don't POST
544
+ });
472
545
  ```
473
546
 
547
+ One row per (event × subscription), deterministic id so an at-least-once upstream enqueues each delivery exactly once. Transient failures (network/timeout/429/5xx) retry with backoff; 4xx dead-letters on the FIRST attempt. `durable` + `retry` together is a registration error.
548
+
474
549
  Full event recipes (retry, DLQ, dual-publish warnings, idempotency keys) → [references/events.md](references/events.md).
475
550
 
476
551
  ## Hooks
@@ -652,6 +727,23 @@ await app.audit.custom('order', req.params.id, 'refund', { reason }, { user });
652
727
 
653
728
  `@classytic/arc/usage` — `usagePlugin` decorates `fastify.usage` with per-actor, per-UTC-month counters (`record`/`summary`/`period`/`actorOf`; actor chain org → user → client → ip). Auto-tracks `api.requests` (default on) + `api.egress.bytes` (opt-in); recording is fail-safe (a throwing `UsageStore` never fails a request; `MemoryUsageStore` built in, Redis/kit backends are ~5 lines). Pair with `createApp({ rateLimit: { plan: { resolve, limits: { free: { max: 60 }, enterprise: false }, default: 'free' } } })` for per-plan ceilings — `false` = unlimited, unknown/throwing resolvers fall back to `default` then global `max`, and buckets follow the tenant key chain by default. Caveat: the limiter runs before route auth, so `resolve` sees the raw request.
654
729
 
730
+ ## Runtime capability registry (2.34)
731
+
732
+ Under `runtime: 'distributed'`, subsystems holding process-local state must declare it — the end-of-boot audit FAILS the app on undeclared memory state, naming every violator:
733
+
734
+ ```typescript
735
+ import { declareRuntimeCapability } from '@classytic/arc/utils';
736
+
737
+ declareRuntimeCapability(fastify, {
738
+ subsystem: 'billing.sequence-cache',
739
+ durability: 'memory', // 'memory' | 'shared'
740
+ accepted: true, // per-process BY DESIGN (logged, never fatal)
741
+ detail: 'short-TTL cache; correctness from TTL, not shared state',
742
+ });
743
+ ```
744
+
745
+ `createApp`'s constructor-time guard only sees what it is passed; state a host wires inside `plugins()` was invisible to it and lived on a checklist. This makes the checklist enforcement. Arc's webhooks plugin declares its default in-memory store.
746
+
655
747
  ## Adapters
656
748
 
657
749
  Cross-framework adapter contract lives in `@classytic/repo-core/adapter`. Every kit-specific adapter ships from its kit's `/adapter` subpath; arc itself has zero kit-bound adapters.
@@ -126,8 +126,27 @@ import {
126
126
  import { tracingPlugin } from '@classytic/arc/plugins/tracing';
127
127
  import { auditPlugin } from '@classytic/arc/audit';
128
128
  import { idempotencyPlugin } from '@classytic/arc/idempotency';
129
+ // Runtime capability registry (2.34) — declare process-local state so a
130
+ // `runtime: 'distributed'` boot fails on undeclared memory state:
131
+ import { declareRuntimeCapability } from '@classytic/arc/utils';
129
132
  ```
130
133
 
134
+ **Idempotency store — the one schema mistake that matters.** Backing the store
135
+ with a repository requires the key path to survive the filter. A Mongoose
136
+ schema with NO declared paths under a global `strictQuery: true` has EVERY
137
+ filter key stripped, so `getOne({ _id: key })` becomes `getOne({})`: one row
138
+ absorbs every key and responses replay across keys AND users. Declare the path
139
+ and pin the option on the schema:
140
+
141
+ ```typescript
142
+ new Schema({ _id: String }, { strict: false, strictQuery: false, timestamps: false })
143
+ // NOT `new Schema({}, { _id: false, strict: false })`
144
+ ```
145
+
146
+ The plugin's boot self-check (default on, `selfCheck: false` to disable) probes
147
+ for exactly this and refuses to register; the adapter also throws
148
+ `IdempotencyStoreMisconfiguredError` on any cross-key read at runtime.
149
+
131
150
  ## Integrations
132
151
 
133
152
  ```typescript
@@ -292,7 +292,7 @@ field masking. Client = plain `EventSource` with `?token=`. No replay — reconn
292
292
  refetches the list. Choosing: per-user resource views → realtime; bidirectional/geo
293
293
  → `integrations/websocket` (its CRUD bridge is org-coarse — never expose
294
294
  row-permissioned resources through it); event firehose → sse. Massive fan-out
295
- (viral feeds, live scores) → publish once to an edge layer (Ably/Centrifugo/CF)
295
+ (viral feeds, live scores) → publish once to a dedicated edge fan-out layer
296
296
  via `app.events.subscribe(...)`; full tiering guide: docs/production-ops/realtime.mdx.
297
297
 
298
298
  ```typescript
@@ -618,7 +618,7 @@ presets: [bulkPreset({ operations: ['createMany', 'updateMany'], maxCreateItems:
618
618
  ## Compensating Transaction
619
619
 
620
620
  In-process rollback primitive — runs steps in order, compensates in reverse on failure.
621
- For distributed sagas across services, use Temporal, Inngest, or Streamline.
621
+ For distributed sagas across services, use `@classytic/streamline` or a dedicated workflow orchestrator.
622
622
 
623
623
  ```typescript
624
624
  import { withCompensation } from '@classytic/arc/utils';
@@ -1,85 +0,0 @@
1
-
2
- import { hn as HookSystem, z as ResourceRegistry } from "./index-CpzxqePp.mjs";
3
- import { t as ExternalOpenApiPaths } from "./externalPaths-CmGOxOtD.mjs";
4
- import { h as HealthCheck, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-CBgvkp8O.mjs";
5
- import { s as ModuleState } from "./index-CFThn1K9.mjs";
6
- import { FastifyPluginAsync } from "fastify";
7
- //#region src/core/arcCorePlugin.d.ts
8
- interface ArcCorePluginOptions {
9
- /** Enable event emission for CRUD operations (requires eventPlugin) */
10
- emitEvents?: boolean;
11
- /** Hook system instance (for testing/custom setup) */
12
- hookSystem?: HookSystem;
13
- /** Resource registry instance (for testing/custom setup) */
14
- registry?: ResourceRegistry;
15
- }
16
- interface PluginMeta {
17
- name: string;
18
- version?: string;
19
- options?: Record<string, unknown>;
20
- registeredAt: string;
21
- }
22
- interface ArcCore {
23
- /** Instance-scoped hook system */
24
- hooks: HookSystem;
25
- /** Instance-scoped resource registry */
26
- registry: ResourceRegistry;
27
- /** Whether event emission is enabled */
28
- emitEvents: boolean;
29
- /** External OpenAPI paths contributed by auth adapters or third-party integrations */
30
- externalOpenApiPaths: ExternalOpenApiPaths[];
31
- /** Registered plugins for introspection */
32
- plugins: Map<string, PluginMeta>;
33
- /**
34
- * Module public exports — `bootstrap()` return values keyed by module name
35
- * (see `src/factory/module.ts`). Read via `getModuleExports(fastify, name)`:
36
- * augment `ArcModuleRegistry` once and the name infers the export type;
37
- * otherwise pass it inline. `Partial<ArcModuleRegistry>` gives augmented
38
- * apps typed direct access (`fastify.arc.modules.order`), while the
39
- * `Record<string, unknown>` intersection keeps un-augmented reads open.
40
- * Populated lazily by `registerResources` — absent until a module exports.
41
- */
42
- modules?: Partial<ArcModuleRegistry> & Record<string, unknown>;
43
- /**
44
- * Lifecycle state per composed module (null-proto map, populated by
45
- * `registerResources` the moment the graph is validated — so it answers
46
- * "is this module composed?" even for resource-only modules that never
47
- * record an export). Read via `hasModule(fastify, name)` /
48
- * `getModuleState(fastify, name)`; the map itself is internal.
49
- */
50
- moduleStates?: Record<string, ModuleState>;
51
- /**
52
- * Module-contributed readiness checks, collected in dependency order and
53
- * frozen by `createApp`. The API role merges these into its `healthPlugin`;
54
- * `createWorker` reads this to give its probe listener the identical union
55
- * (the worker preset disables the main health plugin, so this is how module
56
- * checks reach the probe without a double registration). Empty until
57
- * `createApp` populates it.
58
- */
59
- healthChecks?: readonly HealthCheck[];
60
- /**
61
- * The resolved, dependency-ordered module definitions (frozen by createApp).
62
- * Integrations that collect a module arm at their OWN init time — e.g. the
63
- * streamline integration calling `collectModuleWorkflows(fastify, defs)` after
64
- * it has created + decorated its container — read the list from here. Empty
65
- * until createApp populates it.
66
- */
67
- moduleDefinitions?: readonly ArcModule[];
68
- /**
69
- * Module-contributed recurring schedules, resolved once in dependency order
70
- * and frozen after bootstraps. The same definitions are executed by Arc's
71
- * schedules plugin and exposed here for introspection. Empty until collection.
72
- */
73
- scheduledJobs?: readonly Readonly<ScheduleDefinition>[];
74
- }
75
- declare const arcCorePlugin: FastifyPluginAsync<ArcCorePluginOptions>;
76
- declare const _default: FastifyPluginAsync<ArcCorePluginOptions>;
77
- //#endregion
78
- //#region src/types/fastify-augmentation.d.ts
79
- declare module "fastify" {
80
- interface FastifyInstance {
81
- arc?: ArcCore;
82
- }
83
- }
84
- //#endregion
85
- export { arcCorePlugin as a, _default as i, ArcCorePluginOptions as n, PluginMeta as r, ArcCore as t };
@@ -1,18 +0,0 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { c as getOrgId, g as getUserId, x as isMember } from "./types-Cys0ko9Z.mjs";
3
- //#region src/scope/projection.ts
4
- /**
5
- * Compute the request-scope projection. Returns `undefined` when no
6
- * scope is attached (public / unscoped routes) so hosts can idiomatically
7
- * write `ctx.scope?.organizationId` without a double-null check.
8
- */
9
- function buildRequestScopeProjection(scope) {
10
- if (!scope) return void 0;
11
- return {
12
- organizationId: getOrgId(scope),
13
- userId: getUserId(scope),
14
- orgRoles: isMember(scope) ? scope.orgRoles : void 0
15
- };
16
- }
17
- //#endregion
18
- export { buildRequestScopeProjection as t };