@classytic/arc 2.16.0 → 2.17.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 (140) hide show
  1. package/README.md +22 -2
  2. package/dist/{BaseController-DlCCTIxJ.mjs → BaseController-Dmeh4Y_y.mjs} +5 -47
  3. package/dist/{QueryCache-SvmT_9ti.d.mts → QueryCache-8YmAQ-zW.d.mts} +1 -1
  4. package/dist/{ResourceRegistry-f48hFk3m.mjs → ResourceRegistry-CzSzVK1m.mjs} +3 -0
  5. package/dist/audit/index.d.mts +1 -1
  6. package/dist/audit/index.mjs +2 -1
  7. package/dist/auth/index.d.mts +4 -4
  8. package/dist/auth/index.mjs +2 -2
  9. package/dist/auth/redis-session.d.mts +1 -1
  10. package/dist/auth/redis-session.mjs +28 -2
  11. package/dist/{buildHandler-BZX6zzDM.mjs → buildHandler-C_SkBNF3.mjs} +6 -4
  12. package/dist/cache/index.d.mts +3 -3
  13. package/dist/cli/commands/describe.d.mts +1 -1
  14. package/dist/cli/commands/describe.mjs +1 -1
  15. package/dist/cli/commands/docs.mjs +2 -2
  16. package/dist/cli/commands/generate.d.mts +1 -1
  17. package/dist/cli/commands/generate.mjs +1 -1
  18. package/dist/cli/commands/init.d.mts +1 -1
  19. package/dist/cli/commands/init.mjs +1 -1
  20. package/dist/cli/commands/introspect.mjs +1 -1
  21. package/dist/cli/index.d.mts +2 -2
  22. package/dist/cli/index.mjs +2 -2
  23. package/dist/core/index.d.mts +2 -2
  24. package/dist/core/index.mjs +3 -3
  25. package/dist/{core-DBJ_j6rX.mjs → core-DYJvu7aI.mjs} +174 -31
  26. package/dist/{createActionRouter-DUpN3Dd1.mjs → createActionRouter-BHUMb1-y.mjs} +6 -3
  27. package/dist/{createAggregationRouter-Dq-TUCuY.mjs → createAggregationRouter-BYVvhfc5.mjs} +3 -3
  28. package/dist/{createApp-DNccuhyI.mjs → createApp-CzCEEsEm.mjs} +9 -9
  29. package/dist/{defineEvent-DRwY0fYm.mjs → defineEvent-NPKUJRt8.mjs} +1 -1
  30. package/dist/docs/index.d.mts +2 -2
  31. package/dist/docs/index.mjs +2 -2
  32. package/dist/{elevation-Dci0AYLT.mjs → elevation-6mfTLBC-.mjs} +1 -1
  33. package/dist/{errorHandler-DpoXQHZ9.mjs → errorHandler-wQejMBT3.mjs} +45 -4
  34. package/dist/{eventPlugin-C2cGqtRO.mjs → eventPlugin-CnJCUyM0.mjs} +1 -1
  35. package/dist/{eventPlugin-CtHC_av1.d.mts → eventPlugin-DjEx_J3j.d.mts} +1 -1
  36. package/dist/events/index.d.mts +3 -3
  37. package/dist/events/index.mjs +5 -5
  38. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  39. package/dist/events/transports/redis-stream-entry.mjs +34 -1
  40. package/dist/events/transports/redis.d.mts +1 -1
  41. package/dist/events/transports/redis.mjs +17 -1
  42. package/dist/factory/index.d.mts +2 -2
  43. package/dist/factory/index.mjs +4 -3
  44. package/dist/fastify-augmentation-7LIR0nDv.d.mts +42 -0
  45. package/dist/{fields-Anj0xdih.d.mts → fields-DqZLp5d6.d.mts} +1 -1
  46. package/dist/hooks/index.d.mts +1 -1
  47. package/dist/idempotency/index.d.mts +2 -2
  48. package/dist/idempotency/index.mjs +21 -5
  49. package/dist/idempotency/redis.d.mts +1 -1
  50. package/dist/{index-3oIimXQn.d.mts → index-3L-adFJO.d.mts} +3 -3
  51. package/dist/{index-CkW0flkU.d.mts → index-C1JSBan-.d.mts} +256 -8
  52. package/dist/{index-B-ulKx5P.d.mts → index-CruZir1o.d.mts} +36 -16
  53. package/dist/index.d.mts +5 -5
  54. package/dist/index.mjs +5 -5
  55. package/dist/integrations/event-gateway.d.mts +2 -2
  56. package/dist/integrations/event-gateway.mjs +1 -1
  57. package/dist/integrations/index.d.mts +2 -2
  58. package/dist/integrations/jobs.d.mts +36 -1
  59. package/dist/integrations/jobs.mjs +82 -9
  60. package/dist/integrations/mcp/index.d.mts +67 -5
  61. package/dist/integrations/mcp/index.mjs +93 -2
  62. package/dist/integrations/mcp/testing.d.mts +1 -1
  63. package/dist/integrations/mcp/testing.mjs +1 -1
  64. package/dist/integrations/websocket-redis.d.mts +1 -1
  65. package/dist/integrations/websocket.d.mts +1 -1
  66. package/dist/integrations/websocket.mjs +2 -1
  67. package/dist/{loadResourcesFromEntry-BLMEI2Xa.mjs → loadResourcesFromEntry-DCjYMgfC.mjs} +1 -1
  68. package/dist/{mcpPlugin-7vGV51ED.mjs → mcpPlugin-hZE00H74.mjs} +114 -11
  69. package/dist/middleware/index.d.mts +1 -1
  70. package/dist/middleware/index.mjs +1 -1
  71. package/dist/{multipartBody-BOvVSVCD.mjs → multipartBody-VjVWmZl8.mjs} +2 -1
  72. package/dist/{openapi-34T9yNwd.mjs → openapi-DKDs9NfD.mjs} +37 -10
  73. package/dist/permissions/index.d.mts +2 -2
  74. package/dist/permissions/index.mjs +2 -2
  75. package/dist/{permissions-CTxMrreC.mjs → permissions-B1Qy55fg.mjs} +169 -169
  76. package/dist/pipeline/index.d.mts +1 -1
  77. package/dist/plugins/index.d.mts +5 -59
  78. package/dist/plugins/index.mjs +7 -7
  79. package/dist/plugins/tracing-entry.d.mts +1 -1
  80. package/dist/plugins/tracing-entry.mjs +1 -1
  81. package/dist/preloadResources-BOXzPhKU.mjs +45 -0
  82. package/dist/presets/filesUpload.d.mts +4 -4
  83. package/dist/presets/filesUpload.mjs +2 -2
  84. package/dist/presets/index.d.mts +1 -1
  85. package/dist/presets/index.mjs +1 -1
  86. package/dist/presets/multiTenant.d.mts +1 -1
  87. package/dist/presets/search.d.mts +2 -2
  88. package/dist/presets/search.mjs +1 -1
  89. package/dist/{presets-C9BE6WaZ.mjs → presets-BYbPobbF.mjs} +5 -3
  90. package/dist/{queryCachePlugin-Biqzfbi5.d.mts → queryCachePlugin-Do4P_WBC.d.mts} +2 -2
  91. package/dist/{redis-Cyzrz6SX.d.mts → redis-cpqVb85w.d.mts} +1 -1
  92. package/dist/{redis-stream-DT-YjzrB.d.mts → redis-stream-BuiAS3aE.d.mts} +1 -1
  93. package/dist/registry/index.d.mts +30 -8
  94. package/dist/registry/index.mjs +2 -2
  95. package/dist/{registry-BBE23CDj.mjs → registry-BrlHkrRS.mjs} +14 -8
  96. package/dist/{routerShared-CZV5aabX.mjs → routerShared-nCyJGOKX.mjs} +164 -17
  97. package/dist/scope/index.d.mts +2 -2
  98. package/dist/scope/index.mjs +2 -2
  99. package/dist/testing/index.d.mts +2 -25
  100. package/dist/testing/index.mjs +2 -45
  101. package/dist/testing/storageContract.d.mts +1 -1
  102. package/dist/types/index.d.mts +4 -4
  103. package/dist/types/storage.d.mts +1 -1
  104. package/dist/{types-D-fYtKjb.d.mts → types-BRmS8TNa.d.mts} +34 -11
  105. package/dist/{types-BsJMEQ4D.d.mts → types-CoMjNmkF.d.mts} +12 -1
  106. package/dist/utils/index.d.mts +83 -5
  107. package/dist/utils/index.mjs +2 -2
  108. package/dist/{utils-DC5ycPfr.mjs → utils-CvD3glpM.mjs} +168 -3
  109. package/dist/{validate-By96rH0r.mjs → validate-DFIP2VrM.mjs} +65 -3
  110. package/dist/{versioning-ZwX9tmbS.d.mts → versioning-5H4930tj.d.mts} +1 -1
  111. package/package.json +3 -3
  112. package/skills/arc/SKILL.md +43 -1
  113. package/skills/arc/references/api-reference.md +2 -0
  114. package/skills/arc/references/mcp.md +30 -1
  115. /package/dist/{EventTransport-C-2oAHtw.d.mts → EventTransport-B1GFiYUy.d.mts} +0 -0
  116. /package/dist/{EventTransport-Hxvv5QQz.mjs → EventTransport-ClEDfiMS.mjs} +0 -0
  117. /package/dist/{caching-TeHE8G-v.mjs → caching-8MfrrUYU.mjs} +0 -0
  118. /package/dist/{elevation-0YBpa663.d.mts → elevation-DvwtjDwu.d.mts} +0 -0
  119. /package/dist/{errorHandler-mHuyWzZE.d.mts → errorHandler-DuqTqeqn.d.mts} +0 -0
  120. /package/dist/{errors-C1lX_jlm.d.mts → errors-4ZloYFvm.d.mts} +0 -0
  121. /package/dist/{externalPaths-DFg-2KTp.d.mts → externalPaths-ClykOQqa.d.mts} +0 -0
  122. /package/dist/{generate-CYac-OLv.mjs → generate-CcM24hoq.mjs} +0 -0
  123. /package/dist/{generate-BWFwgcCM.d.mts → generate-cRnsafMS.d.mts} +0 -0
  124. /package/dist/{init-Dv71MsJr.d.mts → init-D-vrTZ1G.d.mts} +0 -0
  125. /package/dist/{init-HDvoO9L5.mjs → init-DYzfniuX.mjs} +0 -0
  126. /package/dist/{interface-CH0OQudo.d.mts → interface-BVkZsvEe.d.mts} +0 -0
  127. /package/dist/{interface-NwJ_qPlY.d.mts → interface-DoE6UoC2.d.mts} +0 -0
  128. /package/dist/{loadResources-ChQEj8ih.mjs → loadResources-fol6JuvW.mjs} +0 -0
  129. /package/dist/{metrics-TuOmguhi.mjs → metrics-B-PU4-Yu.mjs} +0 -0
  130. /package/dist/{replyHelpers-C-gD32oF.mjs → replyHelpers-boS5Rx26.mjs} +0 -0
  131. /package/dist/{schemaIR-Ctc89DSn.mjs → schemaIR-DAOvBWgT.mjs} +0 -0
  132. /package/dist/{sessionManager-BqFegc0W.d.mts → sessionManager-DlmaHUAl.d.mts} +0 -0
  133. /package/dist/{sse-BY6sTy4P.mjs → sse-jh5OqFjR.mjs} +0 -0
  134. /package/dist/{storage-D2KZJAmn.d.mts → storage-HcAgP5Ni.d.mts} +0 -0
  135. /package/dist/{store-helpers-B0sunfZZ.mjs → store-helpers-5JgaxieH.mjs} +0 -0
  136. /package/dist/{tracing-Dm8n7Cnn.d.mts → tracing-bRqqUxFO.d.mts} +0 -0
  137. /package/dist/{types-DVfpSfx2.d.mts → types-CfapW2t8.d.mts} +0 -0
  138. /package/dist/{types-D57iXYb8.mjs → types-DV9WDfeg.mjs} +0 -0
  139. /package/dist/{versioning-B6mimogM.mjs → versioning-CuIwUQZ2.mjs} +0 -0
  140. /package/dist/{websocket-BkjeGZRn.d.mts → websocket-BJ0w-3Dt.d.mts} +0 -0
package/README.md CHANGED
@@ -122,12 +122,31 @@ export default defineResource({
122
122
  actions: {
123
123
  approve: { handler: approveOrder, permissions: requireRoles(['admin']) },
124
124
  },
125
- // mcp: false, // opt out of MCP tool generation for this resource (2.16)
125
+ // mcp: false, // opt out of MCP tool generation for this resource
126
126
  });
127
127
  ```
128
128
 
129
129
  Auto-generates: `GET /products`, `GET /products/:id`, `POST /products`, `PATCH /products/:id`, `DELETE /products/:id` + softDelete adds `GET /products/deleted`, `POST /products/:id/restore` + slugLookup adds `GET /products/by-slug/:slug` + custom routes + `POST /products/:id/action`.
130
130
 
131
+ ### Resource shorthands
132
+
133
+ ```typescript
134
+ // Reference data — read-only list+get, fetch-all limits (1000), 5min/10min cache.
135
+ defineResource({ name: 'currency', adapter, referenceData: true });
136
+
137
+ // Service resource — custom routes only, no adapter, no auto-CRUD, no registry entry.
138
+ defineResource({
139
+ name: 'health',
140
+ customRoutesOnly: true,
141
+ routes: [{ method: 'GET', path: '/ping', permissions: allowPublic(), handler: () => ({ ok: true }) }],
142
+ });
143
+
144
+ // Tune pagination caps inline (no custom queryParser needed).
145
+ defineResource({ name: 'pipeline', adapter, defaultLimit: 200, maxLimit: 500 });
146
+ ```
147
+
148
+ `?limit=` violations respond 400 with a cap-aware envelope: `message: "Query parameter 'limit' must be <= 500 (got 800) (cap is 500)"` plus `meta: { field: 'limit', cap: 500 }` so callers can self-correct without scraping the message.
149
+
131
150
  ---
132
151
 
133
152
  ## Permissions
@@ -243,7 +262,8 @@ Tree-shake by importing only the subpath you need:
243
262
  | `@classytic/arc/plugins` | Health, request-id, versioning, tracing, response-cache |
244
263
  | `@classytic/arc/integrations/jobs` | BullMQ job dispatcher |
245
264
  | `@classytic/arc/integrations/websocket` | WebSocket integration |
246
- | `@classytic/arc/mcp` | Model Context Protocol tools |
265
+ | `@classytic/arc/mcp` | Model Context Protocol tools + `createMcpAuthFromBetterAuthApiKey` (Better Auth API-key resolver), `resolveToolCollisions` |
266
+ | `@classytic/arc/utils` | `pipeUIMessageStreamToReply`, `UI_MESSAGE_STREAM_HEADERS` (Vercel AI SDK → Fastify reply); errors, query parser, circuit breaker |
247
267
  | `@classytic/arc/testing` | `createTestApp`, `expectArc`, `TestAuthProvider`, `createTestFixtures` |
248
268
  | `@classytic/arc/types` | Type-only barrel (zero runtime cost) |
249
269
 
@@ -1,10 +1,9 @@
1
- import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-TrJVIJl0.mjs";
1
+ import { o as DEFAULT_TENANT_FIELD } from "./constants-TrJVIJl0.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
3
  import { d as createDomainError, f as createError, i as NotFoundError, r as ForbiddenError } from "./errors-j4aJm1Wg.mjs";
4
- import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-Bi0r0vjG.mjs";
5
- import { N as simpleEqualityMatcher, _ as ArcQueryParser, r as scheduleBackground, t as getUserId } from "./utils-DC5ycPfr.mjs";
6
- import { d as applyFieldWritePermissions, p as resolveEffectiveRoles } from "./permissions-CTxMrreC.mjs";
7
- import { t as getUserRoles } from "./types-D57iXYb8.mjs";
4
+ import { b as isElevated, c as getOrgId } from "./types-Bi0r0vjG.mjs";
5
+ import { I as simpleEqualityMatcher, b as ArcQueryParser, o as scheduleBackground, t as getUserId } from "./utils-CvD3glpM.mjs";
6
+ import { w as BodySanitizer } from "./routerShared-nCyJGOKX.mjs";
8
7
  import { t as buildQueryKey } from "./keys-DopsCuyQ.mjs";
9
8
  //#region src/core/AccessControl.ts
10
9
  const log = arcLog("access-control");
@@ -241,47 +240,6 @@ var AccessControl = class {
241
240
  return req.metadata;
242
241
  }
243
242
  };
244
- var BodySanitizer = class {
245
- schemaOptions;
246
- onFieldWriteDenied;
247
- constructor(config) {
248
- this.schemaOptions = config.schemaOptions;
249
- this.onFieldWriteDenied = config.onFieldWriteDenied ?? "reject";
250
- }
251
- /**
252
- * Strip readonly and system-managed fields from request body.
253
- * Prevents clients from overwriting _id, timestamps, __v, etc.
254
- *
255
- * Also applies field-level write permissions when the request has
256
- * field permission metadata.
257
- */
258
- sanitize(body, _operation, req, meta) {
259
- let sanitized = { ...body };
260
- for (const field of SYSTEM_FIELDS) delete sanitized[field];
261
- const scopeForRules = req ? (meta ?? req.metadata)?._scope ?? PUBLIC_SCOPE : void 0;
262
- const scopeIsElevated = scopeForRules ? isElevated(scopeForRules) : false;
263
- const fieldRules = this.schemaOptions.fieldRules ?? {};
264
- for (const [field, rules] of Object.entries(fieldRules)) {
265
- const bypass = Boolean(rules.preserveForElevated) && scopeIsElevated;
266
- if ((rules.systemManaged || rules.readonly) && !bypass) delete sanitized[field];
267
- if (_operation === "update" && (rules.immutable || rules.immutableAfterCreate) && !bypass) delete sanitized[field];
268
- }
269
- if (req) {
270
- const arcContext = meta ?? req.metadata;
271
- const scope = arcContext?._scope ?? PUBLIC_SCOPE;
272
- if (!isElevated(scope)) {
273
- const fieldPerms = arcContext?.arc?.fields;
274
- if (fieldPerms) {
275
- const effectiveRoles = resolveEffectiveRoles(getUserRoles(req.user), isMember(scope) ? scope.orgRoles : []);
276
- const { body: filtered, deniedFields } = applyFieldWritePermissions(sanitized, fieldPerms, effectiveRoles);
277
- if (deniedFields.length > 0 && this.onFieldWriteDenied === "reject") throw new ForbiddenError(`Not permitted to write field${deniedFields.length === 1 ? "" : "s"}: ${deniedFields.join(", ")}`);
278
- sanitized = filtered;
279
- }
280
- }
281
- }
282
- return sanitized;
283
- }
284
- };
285
243
  //#endregion
286
244
  //#region src/core/fieldRulePredicates.ts
287
245
  /**
@@ -1531,4 +1489,4 @@ function TreeMixin(Base) {
1531
1489
  */
1532
1490
  var BaseController = class extends SoftDeleteMixin(TreeMixin(SlugMixin(BulkMixin(BaseCrudController)))) {};
1533
1491
  //#endregion
1534
- export { BulkMixin as a, collectReadBlockedFields as c, AccessControl as d, SlugMixin as i, isFieldReadable as l, TreeMixin as n, BaseCrudController as o, SoftDeleteMixin as r, QueryResolver as s, BaseController as t, BodySanitizer as u };
1492
+ export { BulkMixin as a, collectReadBlockedFields as c, SlugMixin as i, isFieldReadable as l, TreeMixin as n, BaseCrudController as o, SoftDeleteMixin as r, QueryResolver as s, BaseController as t, AccessControl as u };
@@ -1,4 +1,4 @@
1
- import { r as CacheStore } from "./interface-CH0OQudo.mjs";
1
+ import { r as CacheStore } from "./interface-BVkZsvEe.mjs";
2
2
 
3
3
  //#region src/cache/QueryCache.d.ts
4
4
  /** Metadata wrapper stored in CacheStore */
@@ -96,6 +96,9 @@ var ResourceRegistry = class {
96
96
  lookupAliases: (entry.lookups ?? []).map((l) => l.as ?? l.from),
97
97
  requireDateRange: entry.requireDateRange,
98
98
  requireFilters: entry.requireFilters,
99
+ limit: entry.limit,
100
+ defaultLimit: entry.defaultLimit,
101
+ maxLimit: entry.maxLimit,
99
102
  mcp: entry.mcp
100
103
  })) : void 0,
101
104
  plugin: resource.toPlugin()
@@ -1,4 +1,4 @@
1
- import { d as UserBase } from "../fields-Anj0xdih.mjs";
1
+ import { d as UserBase } from "../fields-DqZLp5d6.mjs";
2
2
  import { FastifyPluginAsync } from "fastify";
3
3
  import { RepositoryLike } from "@classytic/repo-core/adapter";
4
4
 
@@ -351,7 +351,8 @@ function createNoopLogger() {
351
351
  restore: noop,
352
352
  custom: noop,
353
353
  query: async () => [],
354
- purge: async () => 0
354
+ purge: async () => 0,
355
+ _noop: true
355
356
  };
356
357
  }
357
358
  var auditPlugin_default = fp(auditPlugin, {
@@ -1,7 +1,7 @@
1
- import { Bt as AuthHelpers, Vt as AuthPluginOptions } from "../index-CkW0flkU.mjs";
2
- import { c as PermissionCheck } from "../fields-Anj0xdih.mjs";
3
- import { t as ExternalOpenApiPaths } from "../externalPaths-DFg-2KTp.mjs";
4
- import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BqFegc0W.mjs";
1
+ import { Bt as AuthHelpers, Vt as AuthPluginOptions } from "../index-C1JSBan-.mjs";
2
+ import { c as PermissionCheck } from "../fields-DqZLp5d6.mjs";
3
+ import { t as ExternalOpenApiPaths } from "../externalPaths-ClykOQqa.mjs";
4
+ import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-DlmaHUAl.mjs";
5
5
  import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
6
6
 
7
7
  //#region src/auth/authPlugin.d.ts
@@ -1,6 +1,6 @@
1
1
  import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-j4aJm1Wg.mjs";
2
- import { _ as requireOrgMembership, v as requireOrgRole, x as requireTeamMembership } from "../permissions-CTxMrreC.mjs";
3
- import { n as normalizeRoles, t as getUserRoles } from "../types-D57iXYb8.mjs";
2
+ import { _ as requireTeamMembership, m as requireOrgRole, p as requireOrgMembership } from "../permissions-B1Qy55fg.mjs";
3
+ import { n as normalizeRoles, t as getUserRoles } from "../types-DV9WDfeg.mjs";
4
4
  import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-ClWxaceA.mjs";
5
5
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
6
6
  import fp from "fastify-plugin";
@@ -1,4 +1,4 @@
1
- import { i as SessionData, s as SessionStore } from "../sessionManager-BqFegc0W.mjs";
1
+ import { i as SessionData, s as SessionStore } from "../sessionManager-DlmaHUAl.mjs";
2
2
 
3
3
  //#region src/auth/redis-session.d.ts
4
4
  /** Minimal Redis client interface — compatible with ioredis */
@@ -1,5 +1,31 @@
1
+ import sjson from "secure-json-parse";
1
2
  //#region src/auth/redis-session.ts
2
3
  /**
4
+ * Redis Session Store for Arc
5
+ *
6
+ * Implements the SessionStore interface using Redis for distributed session storage.
7
+ * Use this in multi-instance/clustered deployments where MemorySessionStore won't work.
8
+ *
9
+ * This is a SEPARATE subpath import — only loaded when explicitly used:
10
+ * import { RedisSessionStore } from '@classytic/arc/auth/redis';
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { createSessionManager } from '@classytic/arc/auth';
15
+ * import { RedisSessionStore } from '@classytic/arc/auth/redis';
16
+ * import Redis from 'ioredis';
17
+ *
18
+ * const redis = new Redis(process.env.REDIS_URL);
19
+ *
20
+ * const sessions = createSessionManager({
21
+ * store: new RedisSessionStore({ redis }),
22
+ * secret: process.env.SESSION_SECRET!,
23
+ * });
24
+ *
25
+ * await fastify.register(sessions.plugin);
26
+ * ```
27
+ */
28
+ /**
3
29
  * Redis-backed session store for distributed deployments.
4
30
  *
5
31
  * Uses two key patterns:
@@ -22,7 +48,7 @@ var RedisSessionStore = class {
22
48
  if (!raw) return null;
23
49
  let session;
24
50
  try {
25
- session = JSON.parse(raw);
51
+ session = sjson.parse(raw);
26
52
  } catch {
27
53
  await this.delete(sessionId);
28
54
  return null;
@@ -46,7 +72,7 @@ var RedisSessionStore = class {
46
72
  async delete(sessionId) {
47
73
  const raw = await this.redis.get(this.prefix + sessionId);
48
74
  if (raw) try {
49
- const session = JSON.parse(raw);
75
+ const session = sjson.parse(raw);
50
76
  await this.redis.srem(this.userPrefix + session.userId, sessionId);
51
77
  } catch {}
52
78
  await this.redis.del(this.prefix + sessionId);
@@ -1,4 +1,4 @@
1
- import { n as compileAggRequest, t as adapterSupportsAggregate } from "./validate-By96rH0r.mjs";
1
+ import { n as compileAggRequest, t as adapterSupportsAggregate } from "./validate-DFIP2VrM.mjs";
2
2
  //#region src/core/aggregation/buildHandler.ts
3
3
  /**
4
4
  * Framework-agnostic aggregation execution. Runs safety guards,
@@ -24,7 +24,7 @@ async function executeAggregation(normalized, deps, ctx) {
24
24
  status: 400,
25
25
  body: guardError
26
26
  };
27
- const aggReq = compileAggRequest(normalized, extractCallerFilter(query), tenantOptions);
27
+ const aggReq = compileAggRequest(normalized, extractCallerFilter(query), tenantOptions, query);
28
28
  if (config.materialized) {
29
29
  const matCtx = {
30
30
  filter: aggReq.filter,
@@ -259,8 +259,10 @@ function extractCallerFilter(query) {
259
259
  return out;
260
260
  }
261
261
  /**
262
- * Map a kit-thrown error to the framework-agnostic execute response.
263
- * Detects two well-known signals:
262
+ * Map a kit-thrown error to the canonical `AggregationExecuteResponse`
263
+ * shape so the aggregation router can surface a structured failure
264
+ * (status + code + message) instead of leaking raw kit errors.
265
+ * Detects three signals:
264
266
  * - "unsupported" / "not implemented" → 501 with upgrade hint
265
267
  * - timeout markers → 504
266
268
  * - everything else → 500
@@ -1,6 +1,6 @@
1
- import { n as CacheStats, r as CacheStore, t as CacheLogger } from "../interface-CH0OQudo.mjs";
2
- import { a as QueryCacheConfig, i as QueryCache, n as CacheResult, r as CacheStatus, t as CacheEnvelope } from "../QueryCache-SvmT_9ti.mjs";
3
- import { i as queryCachePlugin, n as QueryCacheDefaults, r as QueryCachePluginOptions, t as CrossResourceRule } from "../queryCachePlugin-Biqzfbi5.mjs";
1
+ import { n as CacheStats, r as CacheStore, t as CacheLogger } from "../interface-BVkZsvEe.mjs";
2
+ import { a as QueryCacheConfig, i as QueryCache, n as CacheResult, r as CacheStatus, t as CacheEnvelope } from "../QueryCache-8YmAQ-zW.mjs";
3
+ import { i as queryCachePlugin, n as QueryCacheDefaults, r as QueryCachePluginOptions, t as CrossResourceRule } from "../queryCachePlugin-Do4P_WBC.mjs";
4
4
 
5
5
  //#region src/cache/keys.d.ts
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { V as ResourceDefinition, it as RateLimitConfig, mt as RouteSchemaOptions } from "../../index-CkW0flkU.mjs";
1
+ import { V as ResourceDefinition, it as RateLimitConfig, mt as RouteSchemaOptions } from "../../index-C1JSBan-.mjs";
2
2
 
3
3
  //#region src/cli/commands/describe.d.ts
4
4
  interface DescribedResource {
@@ -1,5 +1,5 @@
1
1
  import { t as CRUD_OPERATIONS } from "../../constants-TrJVIJl0.mjs";
2
- import { n as stringifyMeasureMap } from "../../ResourceRegistry-f48hFk3m.mjs";
2
+ import { n as stringifyMeasureMap } from "../../ResourceRegistry-CzSzVK1m.mjs";
3
3
  import { resolve } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  //#region src/cli/commands/describe.ts
@@ -1,5 +1,5 @@
1
- import { t as buildOpenApiSpec } from "../../openapi-34T9yNwd.mjs";
2
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-BLMEI2Xa.mjs";
1
+ import { t as buildOpenApiSpec } from "../../openapi-DKDs9NfD.mjs";
2
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DCjYMgfC.mjs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { mkdirSync, writeFileSync } from "node:fs";
5
5
  //#region src/cli/commands/docs.ts
@@ -1,2 +1,2 @@
1
- import { n as generate, t as ArcProjectConfig } from "../../generate-BWFwgcCM.mjs";
1
+ import { n as generate, t as ArcProjectConfig } from "../../generate-cRnsafMS.mjs";
2
2
  export { ArcProjectConfig, generate };
@@ -1,2 +1,2 @@
1
- import { t as generate } from "../../generate-CYac-OLv.mjs";
1
+ import { t as generate } from "../../generate-CcM24hoq.mjs";
2
2
  export { generate };
@@ -1,2 +1,2 @@
1
- import { a as ProjectConfig, i as PackageManager, n as DependencyManifest, r as InitOptions, t as init } from "../../init-Dv71MsJr.mjs";
1
+ import { a as ProjectConfig, i as PackageManager, n as DependencyManifest, r as InitOptions, t as init } from "../../init-D-vrTZ1G.mjs";
2
2
  export { DependencyManifest, InitOptions, PackageManager, ProjectConfig, init };
@@ -1,2 +1,2 @@
1
- import { t as init } from "../../init-HDvoO9L5.mjs";
1
+ import { t as init } from "../../init-DYzfniuX.mjs";
2
2
  export { init };
@@ -1,4 +1,4 @@
1
- import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-BLMEI2Xa.mjs";
1
+ import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-DCjYMgfC.mjs";
2
2
  //#region src/cli/commands/introspect.ts
3
3
  function describePermission(value) {
4
4
  if (!value) return "none";
@@ -1,7 +1,7 @@
1
1
  import { describe } from "./commands/describe.mjs";
2
2
  import { exportDocs } from "./commands/docs.mjs";
3
3
  import { doctor } from "./commands/doctor.mjs";
4
- import { n as generate } from "../generate-BWFwgcCM.mjs";
5
- import { t as init } from "../init-Dv71MsJr.mjs";
4
+ import { n as generate } from "../generate-cRnsafMS.mjs";
5
+ import { t as init } from "../init-D-vrTZ1G.mjs";
6
6
  import { introspect } from "./commands/introspect.mjs";
7
7
  export { describe, doctor, exportDocs, generate, init, introspect };
@@ -1,7 +1,7 @@
1
1
  import { describe } from "./commands/describe.mjs";
2
2
  import { exportDocs } from "./commands/docs.mjs";
3
3
  import { doctor } from "./commands/doctor.mjs";
4
- import { t as generate } from "../generate-CYac-OLv.mjs";
5
- import { t as init } from "../init-HDvoO9L5.mjs";
4
+ import { t as generate } from "../generate-CcM24hoq.mjs";
5
+ import { t as init } from "../init-DYzfniuX.mjs";
6
6
  import { introspect } from "./commands/introspect.mjs";
7
7
  export { describe, doctor, exportDocs, generate, init, introspect };
@@ -1,3 +1,3 @@
1
- import { $t as BaseController, At as AggregationRateLimit, B as defineResource, Cn as AccessControlConfig, Ct as AggMeasureShorthand, Dt as AggregationIndexHint, Et as AggregationDateRangeRequirement, Ot as AggregationMaterializedContext, Sn as AccessControl, St as AggMeasureInput, Tt as AggregationConfig, V as ResourceDefinition, an as SlugMixin, bn as BodySanitizer, cn as BaseControllerOptions, dn as ControllerConstructionOptions, en as SoftDeleteExt, fn as QueryResolver, in as SlugExt, jt as AggregationsMap, kt as AggregationMaterializedResult, ln as BaseCrudController, nn as TreeExt, on as BulkExt, pn as QueryResolverConfig, rn as TreeMixin, sn as BulkMixin, tn as SoftDeleteMixin, un as ControllerConfigurableOptions, wt as AggregationCacheConfig, xn as BodySanitizerConfig, yn as ListResult } from "../index-CkW0flkU.mjs";
2
- import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-B-ulKx5P.mjs";
1
+ import { $t as BaseController, At as AggregationRateLimit, B as defineResource, Cn as AccessControlConfig, Ct as AggMeasureShorthand, Dt as AggregationIndexHint, Et as AggregationDateRangeRequirement, Ot as AggregationMaterializedContext, Sn as AccessControl, St as AggMeasureInput, Tt as AggregationConfig, V as ResourceDefinition, an as SlugMixin, bn as BodySanitizer, cn as BaseControllerOptions, dn as ControllerConstructionOptions, en as SoftDeleteExt, fn as QueryResolver, in as SlugExt, jt as AggregationsMap, kt as AggregationMaterializedResult, ln as BaseCrudController, nn as TreeExt, on as BulkExt, pn as QueryResolverConfig, rn as TreeMixin, sn as BulkMixin, tn as SoftDeleteMixin, un as ControllerConfigurableOptions, wt as AggregationCacheConfig, xn as BodySanitizerConfig, yn as ListResult } from "../index-C1JSBan-.mjs";
2
+ import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-CruZir1o.mjs";
3
3
  export { AccessControl, AccessControlConfig, AggMeasureInput, AggMeasureShorthand, AggregationCacheConfig, AggregationConfig, AggregationDateRangeRequirement, AggregationIndexHint, AggregationMaterializedContext, AggregationMaterializedResult, AggregationRateLimit, AggregationsMap, BaseController, BaseControllerOptions, BaseCrudController, BodySanitizer, BodySanitizerConfig, BulkExt, BulkMixin, CRUD_OPERATIONS, ControllerConfigurableOptions, ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugExt, SlugMixin, SoftDeleteExt, SoftDeleteMixin, TreeExt, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };
@@ -1,5 +1,5 @@
1
1
  import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-TrJVIJl0.mjs";
2
- import { a as BulkMixin, c as collectReadBlockedFields, d as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-DlCCTIxJ.mjs";
3
- import { _ as getControllerContext, g as createRequestContext, h as createFastifyHandler, m as createCrudHandlers, v as getControllerScope, y as sendControllerResponse } from "../routerShared-CZV5aabX.mjs";
4
- import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-DBJ_j6rX.mjs";
2
+ import { a as BulkMixin, c as collectReadBlockedFields, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as AccessControl } from "../BaseController-Dmeh4Y_y.mjs";
3
+ import { S as sendControllerResponse, _ as createCrudHandlers, b as getControllerContext, v as createFastifyHandler, w as BodySanitizer, x as getControllerScope, y as createRequestContext } from "../routerShared-nCyJGOKX.mjs";
4
+ import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-DYJvu7aI.mjs";
5
5
  export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };