@classytic/arc 2.21.0 → 2.23.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 (115) hide show
  1. package/dist/{BaseController-BnLYgE9A.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
  2. package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
  3. package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
  4. package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
  5. package/dist/audit/index.d.mts +2 -2
  6. package/dist/auth/index.d.mts +30 -2
  7. package/dist/auth/index.mjs +6 -3
  8. package/dist/cli/commands/describe.d.mts +1 -1
  9. package/dist/cli/commands/describe.mjs +1 -1
  10. package/dist/cli/commands/docs.mjs +2 -2
  11. package/dist/cli/commands/init.mjs +1 -1
  12. package/dist/cli/commands/introspect.mjs +1 -1
  13. package/dist/cli/index.mjs +1 -1
  14. package/dist/core/index.d.mts +2 -2
  15. package/dist/core/index.mjs +3 -3
  16. package/dist/{core-DZ6Xe1jR.mjs → core-DQ6rDTyH.mjs} +126 -68
  17. package/dist/{createActionRouter-BWMDW-dL.mjs → createActionRouter-BuOv-m4Y.mjs} +10 -6
  18. package/dist/{createAggregationRouter-l5mNdYrZ.mjs → createAggregationRouter-CgJv_BU_.mjs} +3 -3
  19. package/dist/{createApp-Bi61XxqU.mjs → createApp-CjOeWJ9_.mjs} +252 -57
  20. package/dist/docs/index.d.mts +1 -1
  21. package/dist/docs/index.mjs +1 -1
  22. package/dist/encryption/index.mjs +9 -5
  23. package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-Bq2OeEQL.mjs} +34 -10
  24. package/dist/{errorHandler-B0e-bq-O.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -0
  25. package/dist/events/index.mjs +5 -2
  26. package/dist/factory/index.d.mts +46 -9
  27. package/dist/factory/index.mjs +64 -13
  28. package/dist/{fastify-augmentation-DafigGQp.d.mts → fastify-augmentation-g_f9omSc.d.mts} +2 -2
  29. package/dist/fields-D3lhgzhD.d.mts +121 -0
  30. package/dist/health-ScyjIVo5.mjs +202 -0
  31. package/dist/hooks/index.d.mts +1 -1
  32. package/dist/idempotency/index.d.mts +7 -0
  33. package/dist/idempotency/index.mjs +7 -17
  34. package/dist/{index-Dtrj6XZk.d.mts → index-BBsvQt7U.d.mts} +1310 -1226
  35. package/dist/{types-DpxoNX5v.d.mts → index-Ba10fdKp.d.mts} +196 -66
  36. package/dist/{index-B5N-NaHs.d.mts → index-CC5ruDGh.d.mts} +3 -3
  37. package/dist/{index-B5PoNbfu.d.mts → index-wMoWBLkn.d.mts} +68 -2
  38. package/dist/index.d.mts +6 -5
  39. package/dist/index.mjs +6 -5
  40. package/dist/{init-0YhHkXGk.mjs → init-CjO4p_q1.mjs} +123 -4
  41. package/dist/integrations/event-gateway.mjs +1 -1
  42. package/dist/integrations/index.d.mts +1 -1
  43. package/dist/integrations/mcp/index.d.mts +67 -4
  44. package/dist/integrations/mcp/index.mjs +1 -1
  45. package/dist/integrations/mcp/testing.d.mts +1 -1
  46. package/dist/integrations/mcp/testing.mjs +1 -1
  47. package/dist/integrations/webhooks.d.mts +131 -7
  48. package/dist/integrations/webhooks.mjs +169 -10
  49. package/dist/interface-n5hpKg1d.mjs +16 -0
  50. package/dist/jsonBody-BlzAoGCe.mjs +43 -0
  51. package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
  52. package/dist/logger/index.d.mts +26 -1
  53. package/dist/logger/index.mjs +39 -1
  54. package/dist/{mcpPlugin-B9XjjNVG.mjs → mcpPlugin-BtzIL_kM.mjs} +312 -39
  55. package/dist/middleware/index.d.mts +1 -1
  56. package/dist/{module-WeuU5jfd.d.mts → module-B4u10iOJ.d.mts} +26 -1
  57. package/dist/{openapi-Kyod_di6.mjs → openapi-jSDx-VDv.mjs} +3 -3
  58. package/dist/permissions/index.d.mts +4 -3
  59. package/dist/permissions/index.mjs +3 -2
  60. package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
  61. package/dist/pipeline/index.d.mts +1 -1
  62. package/dist/plugins/index.d.mts +24 -7
  63. package/dist/plugins/index.mjs +14 -282
  64. package/dist/plugins/response-cache.mjs +1 -0
  65. package/dist/plugins/tracing-entry.mjs +7 -4
  66. package/dist/presets/filesUpload.d.mts +2 -2
  67. package/dist/presets/filesUpload.mjs +1 -1
  68. package/dist/presets/index.d.mts +1 -1
  69. package/dist/presets/index.mjs +1 -1
  70. package/dist/presets/multiTenant.d.mts +1 -1
  71. package/dist/presets/search.d.mts +2 -2
  72. package/dist/presets/search.mjs +1 -1
  73. package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
  74. package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
  75. package/dist/realtime-I8OrfnMR.mjs +375 -0
  76. package/dist/{registerRoute-oGAl0oA8.mjs → registerRoute-CWO6mdYe.mjs} +38 -18
  77. package/dist/registry/index.d.mts +2 -2
  78. package/dist/registry/index.mjs +1 -1
  79. package/dist/requestId-5BhTVzBv.mjs +113 -0
  80. package/dist/schemaConverter-DJvodiHj.mjs +171 -0
  81. package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-Ca7ySNPx.mjs} +2 -2
  82. package/dist/scim/index.mjs +6 -7
  83. package/dist/scope/index.mjs +2 -76
  84. package/dist/scope-LONQ3mfZ.mjs +37 -0
  85. package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
  86. package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
  87. package/dist/sseStream-BQ7Qaynp.mjs +57 -0
  88. package/dist/testing/index.d.mts +83 -3
  89. package/dist/testing/index.mjs +70 -3
  90. package/dist/types/index.d.mts +2 -2
  91. package/dist/{types-DRj-Ut4n.d.mts → types-5OuqeTDX.d.mts} +37 -2
  92. package/dist/{fields-BGqrAa_u.d.mts → types-Csxga7d3.d.mts} +1 -121
  93. package/dist/usage/index.d.mts +142 -0
  94. package/dist/usage/index.mjs +0 -0
  95. package/dist/utils/index.d.mts +101 -19
  96. package/dist/utils/index.mjs +5 -3
  97. package/dist/{utils-CO8a3B_1.mjs → utils-CZyeRXTh.mjs} +80 -82
  98. package/dist/{versioning-DQ4mCvg0.d.mts → versioning-D6PvCwpq.d.mts} +43 -9
  99. package/llms.txt +39 -0
  100. package/package.json +513 -447
  101. package/skills/arc/SKILL.md +14 -8
  102. package/skills/arc/references/api-reference.md +34 -0
  103. package/skills/arc/references/auth.md +21 -4
  104. package/skills/arc/references/mcp.md +1 -1
  105. package/skills/arc/references/production.md +734 -654
  106. package/skills/arc-code-review/SKILL.md +1 -1
  107. package/skills/arc-code-review/references/anti-patterns.md +71 -18
  108. package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
  109. package/skills/arc-code-review/references/migration-recipes.md +7 -7
  110. package/skills/arc-code-review/references/mongokit-migration.md +4 -3
  111. package/skills/arc-module-publishing/SKILL.md +2 -1
  112. package/skills/arc-module-publishing/references/authoring-modules.md +55 -0
  113. package/skills/arc-module-publishing/references/package-manifest.md +4 -2
  114. package/dist/preloadResources-CHw3_oA_.mjs +0 -45
  115. package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
@@ -1,13 +1,13 @@
1
- import { Ut as Authenticator } from "./index-Dtrj6XZk.mjs";
1
+ import { Wt as Authenticator } from "./index-BBsvQt7U.mjs";
2
2
  import { r as CacheStore } from "./interface-C1_WLTMK.mjs";
3
3
  import { n as ElevationOptions } from "./elevation-C9s5hFP_.mjs";
4
4
  import { a as EventTransport } from "./EventTransport-QmY7A258.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
6
6
  import { r as QueryCachePluginOptions } from "./queryCachePlugin-CfYWq-Ak.mjs";
7
- import { t as ErrorHandlerOptions } from "./errorHandler-B0e-bq-O.mjs";
8
- import { l as ResourceLike, n as ArcModuleInput } from "./module-WeuU5jfd.mjs";
7
+ import { t as ErrorHandlerOptions } from "./errorHandler-C6yv4ZTW.mjs";
8
+ import { l as ResourceLike, n as ArcModuleInput } from "./module-B4u10iOJ.mjs";
9
9
  import { t as EventPluginOptions } from "./eventPlugin-A6EbthXR.mjs";
10
- import { _ as HealthOptions, f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-DQ4mCvg0.mjs";
10
+ import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions, x as HealthOptions } from "./versioning-D6PvCwpq.mjs";
11
11
  import { r as IdempotencyStore } from "./interface-Uk8ONJv7.mjs";
12
12
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
13
13
  //#region src/testing/preloadResources.d.ts
@@ -33,30 +33,7 @@ declare function preloadResources(globResult: EagerGlobResult): ResourceLike[];
33
33
  */
34
34
  declare function preloadResourcesAsync(globResult: LazyGlobResult): Promise<ResourceLike[]>;
35
35
  //#endregion
36
- //#region src/factory/types.d.ts
37
- type CorsOptions = Record<string, unknown> & {
38
- origin?: unknown;
39
- credentials?: boolean;
40
- methods?: string[];
41
- allowedHeaders?: string[];
42
- };
43
- type HelmetOptions = Record<string, unknown>;
44
- type RateLimitOpts = Record<string, unknown> & {
45
- max?: number;
46
- timeWindow?: string | number;
47
- /**
48
- * Path patterns to exempt from rate limiting. Supports exact match
49
- * (`/health`) or prefix match with trailing `*` (`/api/auth/*`).
50
- *
51
- * Implemented by synthesising a `@fastify/rate-limit` `allowList`
52
- * function. When combined with a user-supplied `allowList`, both
53
- * are OR-ed together (path OR ip/custom match skips the limit).
54
- *
55
- * Use this for endpoints that are hit frequently but independently
56
- * of user traffic — session heartbeat, webhooks, health probes.
57
- */
58
- skipPaths?: string[];
59
- };
36
+ //#region src/factory/types/auth.d.ts
60
37
  /**
61
38
  * Arc's built-in JWT auth
62
39
  *
@@ -161,13 +138,29 @@ interface JwtAuthOption {
161
138
  * });
162
139
  * ```
163
140
  */
141
+ /** The shape `createBetterAuthAdapter()` returns. */
142
+ interface BetterAuthAdapterResult {
143
+ plugin: FastifyPluginAsync;
144
+ openapi?: ExternalOpenApiPaths;
145
+ }
164
146
  interface BetterAuthOption {
165
147
  type: "betterAuth";
166
- /** Better Auth adapter — pass the result of createBetterAuthAdapter() */
167
- betterAuth: {
168
- plugin: FastifyPluginAsync;
169
- openapi?: ExternalOpenApiPaths;
170
- };
148
+ /**
149
+ * Better Auth adapter — the result of `createBetterAuthAdapter()`, or a
150
+ * sync/async THUNK returning it (2.22). The thunk resolves during auth
151
+ * registration — AFTER `beforeBoot` — for adapters whose construction
152
+ * needs a live DB (Better Auth's `mongodbAdapter(mongoose.connection
153
+ * .getClient().db())` requires an OPEN connection, so an eager value
154
+ * forces hosts back into connect-before-createApp ordering dances):
155
+ *
156
+ * ```typescript
157
+ * createApp({
158
+ * beforeBoot: () => connectDatabase(),
159
+ * auth: { type: 'betterAuth', betterAuth: () => createBetterAuthAdapter({ auth: getAuth() }) },
160
+ * });
161
+ * ```
162
+ */
163
+ betterAuth: BetterAuthAdapterResult | (() => BetterAuthAdapterResult | Promise<BetterAuthAdapterResult>);
171
164
  }
172
165
  /**
173
166
  * Custom auth plugin — full control over authentication setup
@@ -234,6 +227,112 @@ interface CustomAuthenticatorOption {
234
227
  * - `CustomAuthenticatorOption` — A bare authenticate function (`type: 'authenticator'`)
235
228
  */
236
229
  type AuthOption = false | JwtAuthOption | BetterAuthOption | CustomPluginAuthOption | CustomAuthenticatorOption;
230
+ //#endregion
231
+ //#region src/factory/types/plugin-options.d.ts
232
+ /**
233
+ * Plugin-specific option shapes for `createApp()` — under-pressure,
234
+ * multipart, and raw-body plugin configuration.
235
+ */
236
+ interface UnderPressureOptions {
237
+ /** Expose `/_status` route for health checks (default: false) */
238
+ exposeStatusRoute?: boolean;
239
+ /** Event loop lag threshold in ms — requests rejected above this (default: 1000) */
240
+ maxEventLoopDelay?: number;
241
+ /** V8 heap usage threshold in bytes — requests rejected above this */
242
+ maxHeapUsedBytes?: number;
243
+ /** RSS memory threshold in bytes — requests rejected above this */
244
+ maxRssBytes?: number;
245
+ }
246
+ interface MultipartOptions {
247
+ limits?: {
248
+ /** Max file size in bytes (default: Fastify default ~1MB) */
249
+ fileSize?: number;
250
+ /** Max number of files per request */
251
+ files?: number;
252
+ };
253
+ }
254
+ interface RawBodyOptions {
255
+ /** Body field name to store raw body on (default: 'rawBody') */
256
+ field?: string;
257
+ /** Apply to all routes globally (default: false) */
258
+ global?: boolean;
259
+ /** Encoding for raw body string (default: 'utf8') */
260
+ encoding?: string;
261
+ /** Parse raw body before other parsers (default: false) */
262
+ runFirst?: boolean;
263
+ }
264
+ //#endregion
265
+ //#region src/factory/types/security.d.ts
266
+ /**
267
+ * Security-plugin option shapes — CORS / Helmet / rate-limit config for
268
+ * `createApp()`, including plan-aware rate limiting (2.22).
269
+ */
270
+ type CorsOptions = Record<string, unknown> & {
271
+ origin?: unknown;
272
+ credentials?: boolean;
273
+ methods?: string[];
274
+ allowedHeaders?: string[];
275
+ };
276
+ type HelmetOptions = Record<string, unknown>;
277
+ type RateLimitOpts = Record<string, unknown> & {
278
+ max?: number;
279
+ timeWindow?: string | number;
280
+ /**
281
+ * Path patterns to exempt from rate limiting. Supports exact match
282
+ * (`/health`) or prefix match with trailing `*` (`/api/auth/*`).
283
+ *
284
+ * Implemented by synthesising a `@fastify/rate-limit` `allowList`
285
+ * function. When combined with a user-supplied `allowList`, both
286
+ * are OR-ed together (path OR ip/custom match skips the limit).
287
+ *
288
+ * Use this for endpoints that are hit frequently but independently
289
+ * of user traffic — session heartbeat, webhooks, health probes.
290
+ */
291
+ skipPaths?: string[];
292
+ /**
293
+ * Plan-aware limits (2.22) — resolve the caller's plan per request and
294
+ * apply that plan's ceiling instead of the global `max`:
295
+ *
296
+ * ```typescript
297
+ * rateLimit: {
298
+ * timeWindow: '1 minute',
299
+ * plan: {
300
+ * resolve: (req) => lookupPlan(req), // 'free' | 'pro' | ...
301
+ * limits: { free: { max: 60 }, pro: { max: 1000 }, enterprise: false },
302
+ * default: 'free',
303
+ * },
304
+ * }
305
+ * ```
306
+ *
307
+ * `false` = effectively unlimited. When `plan` is set and no
308
+ * `keyGenerator` is supplied, arc defaults to the tenant key chain
309
+ * (org → user → client → IP) so buckets follow actors, not addresses.
310
+ *
311
+ * Timing caveat: the limiter runs before route-level auth, so
312
+ * `req.scope` may be unresolved for token-authenticated calls —
313
+ * `resolve` receives the RAW request and may need to derive the plan
314
+ * from its own header/token/session lookup. Same caveat as
315
+ * `createTenantKeyGenerator`'s IP fallback (documented there).
316
+ */
317
+ plan?: RateLimitPlanConfig;
318
+ };
319
+ /** Plan-aware rate limiting (2.22) — see `rateLimit.plan` on CreateAppOptions. */
320
+ interface RateLimitPlanConfig {
321
+ /**
322
+ * Name the caller's plan from the raw request. Return `undefined` to
323
+ * fall back to `default`. Sync or async; a throwing resolver also
324
+ * falls back (limits must fail-safe, not fail-open to unlimited).
325
+ */
326
+ resolve: (req: import("fastify").FastifyRequest) => string | undefined | Promise<string | undefined>;
327
+ /** Per-plan ceilings within the shared `timeWindow`. `false` = effectively unlimited. */
328
+ limits: Record<string, {
329
+ max: number;
330
+ } | false>;
331
+ /** Plan used when `resolve` returns undefined/unknown or throws. Default: fall back to the global `max`. */
332
+ default?: string;
333
+ }
334
+ //#endregion
335
+ //#region src/factory/types/app-options.d.ts
237
336
  /**
238
337
  * CreateApp Options
239
338
  *
@@ -276,8 +375,18 @@ type AuthOption = false | JwtAuthOption | BetterAuthOption | CustomPluginAuthOpt
276
375
  * ```
277
376
  */
278
377
  interface CreateAppOptions {
279
- /** Environment preset: 'production', 'development', 'testing', or 'edge' */
280
- preset?: "production" | "development" | "testing" | "edge";
378
+ /** Environment preset: 'production', 'development', 'testing', 'edge', or 'worker' */
379
+ preset?: "production" | "development" | "testing" | "edge" | "worker";
380
+ /**
381
+ * Route-mounting switch (2.23) — the worker-role primitive. `false`
382
+ * registers every resource's shared runtime state (registry metadata +
383
+ * adapter, hooks, cache-invalidation rules) WITHOUT mounting any routes,
384
+ * so headless processes keep tenant-purge cascade, per-resource audit,
385
+ * and events metadata intact. Set by `preset: 'worker'` /
386
+ * `createWorker()`; hosts composing a custom worker shape may set it
387
+ * directly. Default: true (routes mount).
388
+ */
389
+ mountRoutes?: boolean;
281
390
  /**
282
391
  * Runtime profile for store backends.
283
392
  * - 'memory' (default): Uses in-memory stores. Suitable for single-instance deployments.
@@ -324,10 +433,50 @@ interface CreateAppOptions {
324
433
  * ```
325
434
  */
326
435
  debug?: boolean | string;
327
- /** Trust proxy headers (X-Forwarded-For, etc.) */
328
- trustProxy?: boolean;
436
+ /**
437
+ * Trust proxy headers (X-Forwarded-For, etc.). Pass-through to Fastify's
438
+ * `trustProxy` server option — all its forms are supported, not just the
439
+ * boolean:
440
+ *
441
+ * - `true` — trust ALL proxies (only safe when the app is never directly
442
+ * reachable; a spoofed `X-Forwarded-For` otherwise forges `request.ip`,
443
+ * which feeds rate-limit keys and audit logs)
444
+ * - `1` — trust exactly one hop (typical single LB / reverse proxy)
445
+ * - `'10.0.0.0/8'` / `['10.0.0.0/8', '172.16.0.0/12']` — trust only these
446
+ * CIDR ranges
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * createApp({ preset: 'production', trustProxy: 1 }); // one LB hop
451
+ * createApp({ preset: 'production', trustProxy: '10.0.0.0/8' }); // VPC proxies
452
+ * ```
453
+ */
454
+ trustProxy?: boolean | string | string[] | number;
329
455
  /** Fastify plugin/onReady timeout in ms (default: 10_000). Raise for slow boot work (index materialisation, WAL replay, external warm-up). */
330
456
  pluginTimeout?: number;
457
+ /**
458
+ * Max time in ms the server waits to RECEIVE an entire request
459
+ * (headers + body). Pass-through to Fastify's `requestTimeout` (Node's
460
+ * `server.requestTimeout`). Default `0` defers to Node's own 300s bound
461
+ * — slow-loris requests are already cut off there, so arc doesn't
462
+ * override; tighten (e.g. `30_000`) for APIs that never accept slow
463
+ * uploads. Response duration (SSE, streaming) is NOT affected.
464
+ */
465
+ requestTimeout?: number;
466
+ /**
467
+ * Max time in ms to establish a connection before the socket is
468
+ * destroyed. Pass-through to Fastify's `connectionTimeout` (Node's
469
+ * `server.timeout`). Default `0` = no limit (Node's `headersTimeout`
470
+ * ~60s still bounds idle pre-request sockets).
471
+ */
472
+ connectionTimeout?: number;
473
+ /**
474
+ * Keep-alive idle timeout in ms (Node's `server.keepAliveTimeout`,
475
+ * default 72_000). MUST be longer than the idle timeout of any load
476
+ * balancer in front of the app (ALB default 60s) or the LB reuses
477
+ * sockets the server just closed → intermittent 502s.
478
+ */
479
+ keepAliveTimeout?: number;
331
480
  /**
332
481
  * Maximum JSON body size in bytes. Pass-through to Fastify's
333
482
  * server-level `bodyLimit` option; default is Fastify's 1 MiB
@@ -523,6 +672,15 @@ interface CreateAppOptions {
523
672
  * Requires emitEvents to be enabled (or events plugin registered).
524
673
  */
525
674
  sse?: SSEOptions | boolean;
675
+ /**
676
+ * Realtime resource change feed (2.22) — `GET /realtime/:resource`
677
+ * streams that resource's created/updated/deleted events over SSE,
678
+ * gated by its own `list` permission with per-event row filtering,
679
+ * tenant scoping, and field-level masking. Default: false (opt-in).
680
+ * Set true for defaults, or pass RealtimeOptions (path, heartbeat,
681
+ * resource allowlist, operations). Requires events.
682
+ */
683
+ realtime?: RealtimeOptions | boolean;
526
684
  /**
527
685
  * QueryCache — TanStack Query-inspired server cache with SWR.
528
686
  * Default: false (opt-in). Set to true for memory store defaults.
@@ -933,33 +1091,5 @@ interface CreateAppOptions {
933
1091
  /** Hook called when the app is shutting down */
934
1092
  onClose?: (fastify: FastifyInstance) => void | Promise<void>;
935
1093
  }
936
- interface UnderPressureOptions {
937
- /** Expose `/_status` route for health checks (default: false) */
938
- exposeStatusRoute?: boolean;
939
- /** Event loop lag threshold in ms — requests rejected above this (default: 1000) */
940
- maxEventLoopDelay?: number;
941
- /** V8 heap usage threshold in bytes — requests rejected above this */
942
- maxHeapUsedBytes?: number;
943
- /** RSS memory threshold in bytes — requests rejected above this */
944
- maxRssBytes?: number;
945
- }
946
- interface MultipartOptions {
947
- limits?: {
948
- /** Max file size in bytes (default: Fastify default ~1MB) */
949
- fileSize?: number;
950
- /** Max number of files per request */
951
- files?: number;
952
- };
953
- }
954
- interface RawBodyOptions {
955
- /** Body field name to store raw body on (default: 'rawBody') */
956
- field?: string;
957
- /** Apply to all routes globally (default: false) */
958
- global?: boolean;
959
- /** Encoding for raw body string (default: 'utf8') */
960
- encoding?: string;
961
- /** Parse raw body before other parsers (default: false) */
962
- runFirst?: boolean;
963
- }
964
1094
  //#endregion
965
- export { CustomPluginAuthOption as a, RawBodyOptions as c, preloadResourcesAsync as d, CustomAuthenticatorOption as i, UnderPressureOptions as l, BetterAuthOption as n, JwtAuthOption as o, CreateAppOptions as r, MultipartOptions as s, AuthOption as t, preloadResources as u };
1095
+ export { AuthOption as a, CustomPluginAuthOption as c, preloadResourcesAsync as d, UnderPressureOptions as i, JwtAuthOption as l, MultipartOptions as n, BetterAuthOption as o, RawBodyOptions as r, CustomAuthenticatorOption as s, CreateAppOptions as t, preloadResources as u };
@@ -1,7 +1,7 @@
1
- import { C as RequestContext, Et as AggregationConfig, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, L as RequestWithExtras, T as CrudRouterOptions, V as ResourceDefinition, bt as IControllerResponse, ht as RouteSchemaOptions, pt as RouteMcpConfig, q as CrudController, qt as AnyRecord, st as ResourceConfig, xt as IRequestContext, yt as IController } from "./index-Dtrj6XZk.mjs";
1
+ import { C as RequestContext, Et as ActionDefinition, F as FastifyWithDecorators, H as CrudController, Jt as AnyRecord, L as RequestWithExtras, Lt as IController, Rt as IControllerResponse, T as CrudRouterOptions, U as ResourceConfig, V as ResourceDefinition, et as RouteSchemaOptions, jt as RouteMcpConfig, tt as ArcFieldRule, ut as AggregationConfig, zt as IRequestContext } from "./index-BBsvQt7U.mjs";
2
2
  import { i as RequestScope } from "./types-DH0GupI7.mjs";
3
- import { c as PermissionCheck } from "./fields-BGqrAa_u.mjs";
4
- import "./fastify-augmentation-DafigGQp.mjs";
3
+ import { t as PermissionCheck } from "./types-Csxga7d3.mjs";
4
+ import "./fastify-augmentation-g_f9omSc.mjs";
5
5
  import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
6
6
  import { z } from "zod";
7
7
  import { DataAdapter } from "@classytic/repo-core/adapter";
@@ -1,6 +1,6 @@
1
1
  import { r as CacheStore, t as CacheLogger } from "./interface-C1_WLTMK.mjs";
2
2
  import { i as RequestScope, n as Mandate } from "./types-DH0GupI7.mjs";
3
- import { c as PermissionCheck, l as PermissionContext, u as PermissionResult } from "./fields-BGqrAa_u.mjs";
3
+ import { n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-Csxga7d3.mjs";
4
4
  import { FastifyReply, FastifyRequest } from "fastify";
5
5
  //#region src/permissions/agent.d.ts
6
6
  /**
@@ -449,6 +449,72 @@ declare function createOrgPermissions(config: {
449
449
  */
450
450
  declare function createDynamicPermissionMatrix(config: DynamicPermissionMatrixConfig): DynamicPermissionMatrix;
451
451
  //#endregion
452
+ //#region src/permissions/grants.d.ts
453
+ /**
454
+ * The grant-mode lattice, weakest → strongest. Holding a mode satisfies
455
+ * every mode at or below it (`write` ⊇ `read` ⊇ `list` ⊇ `see`); `manage`
456
+ * (grant/revoke rights) satisfies everything. Same semantics as Puter's
457
+ * `see < list < read < write` hierarchy with `manage` folded into the
458
+ * lattice top instead of a separate string namespace.
459
+ */
460
+ declare const GRANT_MODES: readonly ["see", "list", "read", "write", "manage"];
461
+ /** One mode from the {@link GRANT_MODES} lattice. */
462
+ type GrantMode = (typeof GRANT_MODES)[number];
463
+ /**
464
+ * Does a HELD mode satisfy a REQUIRED mode? Lattice implication —
465
+ * `modeSatisfies('write', 'read') === true`, `modeSatisfies('see', 'read')
466
+ * === false`. Unknown strings (a corrupted grant row) never satisfy
467
+ * anything: fail-closed.
468
+ */
469
+ declare function modeSatisfies(held: string | undefined, required: GrantMode): boolean;
470
+ /**
471
+ * What a grant `resolve` callback may return:
472
+ *
473
+ * - `boolean` — the host did the whole check itself (already compared modes).
474
+ * - `{ mode }` — the strongest mode the caller holds on `ctx.resourceId`;
475
+ * the combinator applies the lattice against the required mode.
476
+ * - `{ filters }` — list-shaped resolution: row filters that scope the
477
+ * query to visible records (flows through `PermissionResult.filters` →
478
+ * `_policyFilters` → the repository query). Grants access with the
479
+ * filters attached.
480
+ * - `{ mode, filters }` — mode gates; filters ride along when it passes.
481
+ *
482
+ * An empty object (neither `mode` nor `filters`) and a thrown error both
483
+ * DENY — grant resolution is fail-closed, same stance as `isRevoked`.
484
+ */
485
+ type GrantResolution = boolean | {
486
+ mode?: GrantMode | string;
487
+ filters?: Record<string, unknown>;
488
+ /** Optional denial reason surfaced on the 403 (clamped by the framework). */
489
+ reason?: string;
490
+ };
491
+ interface RequireGrantOptions {
492
+ /** The mode this operation needs (lattice-checked against the held mode). */
493
+ mode: GrantMode;
494
+ /**
495
+ * Look up the caller's grant. Receives the full `PermissionContext`
496
+ * (`resourceId` for single-record ops, `request` for headers/scope —
497
+ * e.g. a share-link token). May be sync or async.
498
+ *
499
+ * NOTE: unauthenticated callers are NOT rejected up front — share links
500
+ * grant access to anonymous callers by design. If your grants are
501
+ * session-only, return `false` when `ctx.user` is null (or compose with
502
+ * `requireAuth()` via `allOf`).
503
+ */
504
+ resolve: (ctx: PermissionContext) => GrantResolution | Promise<GrantResolution>;
505
+ /** Roles that skip the grant check entirely (platform admins). */
506
+ bypassRoles?: readonly string[];
507
+ }
508
+ /**
509
+ * Per-record grant gate. Same combinator tier as `requireOwnership` —
510
+ * compose with `anyOf`/`allOf` like any other `PermissionCheck`.
511
+ *
512
+ * Fail-closed contract: resolver throws → denied (logged via
513
+ * `ctx.request.log`); resolution carries neither `mode` nor `filters` →
514
+ * denied; unknown/corrupted mode strings → denied.
515
+ */
516
+ declare function requireGrant(options: RequireGrantOptions): PermissionCheck;
517
+ //#endregion
452
518
  //#region src/permissions/roleHierarchy.d.ts
453
519
  /**
454
520
  * Role Hierarchy — Composable RBAC Inheritance
@@ -706,4 +772,4 @@ declare function fullPublic<TDoc = Record<string, unknown>>(overrides?: Permissi
706
772
  */
707
773
  declare function readOnly<TDoc = Record<string, unknown>>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
708
774
  //#endregion
709
- export { requireOwnership as A, requireMandate as B, createOrgPermissions as C, denyAll as D, anyOf as E, normalizePermissionResult as F, RequireAgentScopeOptions as I, RequireMandateOptions as L, roles as M, when as N, not as O, applyPermissionResult as P, requireAgentScope as R, createDynamicPermissionMatrix as S, allowPublic as T, createRoleHierarchy as _, permissionMatrix as a, DynamicPermissionMatrixConfig as b, publicReadAdminWrite as c, requireOrgMembership as d, requireOrgRole as f, RoleHierarchy as g, requireTeamMembership as h, ownerWithAdminBypass as i, requireRoles as j, requireAuth as k, readOnly as l, requireServiceScope as m, authenticated as n, presets_d_exports as o, requireScopeContext as p, fullPublic as r, publicRead as s, adminOnly as t, requireOrgInScope as u, ConnectEventsOptions as v, allOf as w, PermissionEventBus as x, DynamicPermissionMatrix as y, requireDPoP as z };
775
+ export { allOf as A, applyPermissionResult as B, requireGrant as C, PermissionEventBus as D, DynamicPermissionMatrixConfig as E, requireAuth as F, requireDPoP as G, RequireAgentScopeOptions as H, requireOwnership as I, requireMandate as K, requireRoles as L, anyOf as M, denyAll as N, createDynamicPermissionMatrix as O, not as P, roles as R, modeSatisfies as S, DynamicPermissionMatrix as T, RequireMandateOptions as U, normalizePermissionResult as V, requireAgentScope as W, createRoleHierarchy as _, permissionMatrix as a, GrantResolution as b, publicReadAdminWrite as c, requireOrgMembership as d, requireOrgRole as f, RoleHierarchy as g, requireTeamMembership as h, ownerWithAdminBypass as i, allowPublic as j, createOrgPermissions as k, readOnly as l, requireServiceScope as m, authenticated as n, presets_d_exports as o, requireScopeContext as p, fullPublic as r, publicRead as s, adminOnly as t, requireOrgInScope as u, GRANT_MODES as v, ConnectEventsOptions as w, RequireGrantOptions as x, GrantMode as y, when as z };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,9 @@
1
- import { $t as UserOrganization, A as RequestIdOptions, An as ArcCreateResult, At as AggregationMaterializedResult, B as defineResource, C as RequestContext, Ct as AggMeasureInput, D as HealthCheck, Dt as AggregationDateRangeRequirement, E as GracefulShutdownOptions, Et as AggregationConfig, F as FastifyWithDecorators, Fn as BaseControllerOptions, Ht as AuthPluginOptions, In as ControllerConfigurableOptions, J as CrudRouteKey, Jt as ApiResponse, L as RequestWithExtras, Ln as ControllerConstructionOptions, Mn as ArcGetResult, Mt as AggregationsMap, N as FastifyRequestExtras, Nn as ArcListResult, O as HealthOptions, Ot as AggregationIndexHint, P as FastifyWithAuth, Pn as ArcUpdateResult, Q as MiddlewareConfig, Rn as ListResult, S as QueryParserInterface, St as RouteHandler, T as CrudRouterOptions, Tt as AggregationCacheConfig, V as ResourceDefinition, X as EventDefinition, Xt as JWTPayload, Y as CrudSchemas, Yt as ArcRequest, Z as FieldRule, _t as ControllerLike, a as InferAdapterDoc, an as SlugExt, bt as IControllerResponse, c as TypedController, cn as BulkMixin, d as PaginationResult, en as BaseController, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, ht as RouteSchemaOptions, i as ValidationResult, in as TreeMixin, it as RateLimitConfig, jn as ArcDeleteResult, jt as AggregationRateLimit, k as IntrospectionPluginOptions, kt as AggregationMaterializedContext, l as TypedRepository, ln as BaseCrudController, m as RegistryStats, n as ConfigError, nn as SoftDeleteMixin, o as InferDocType, on as SlugMixin, p as RegistryEntry, q as CrudController, qt as AnyRecord, r as ValidateOptions, rn as TreeExt, rt as PresetResult, s as InferResourceDoc, sn as BulkExt, st as ResourceConfig, t as RouteHandlerMethod, tn as SoftDeleteExt, tt as PresetFunction, u as TypedResourceConfig, w as ServiceContext, wt as AggMeasureShorthand, xt as IRequestContext, y as OwnershipCheck, yt as IController } from "./index-Dtrj6XZk.mjs";
2
- import { a as applyFieldWritePermissions, c as PermissionCheck, d as UserBase, i as applyFieldReadPermissions, l as PermissionContext, n as FieldPermissionMap, o as fields, t as FieldPermission, u as PermissionResult } from "./fields-BGqrAa_u.mjs";
3
- import "./fastify-augmentation-DafigGQp.mjs";
4
- import { A as HOOK_OPERATIONS, B as SYSTEM_FIELDS, C as CrudOperation, D as DEFAULT_SORT, E as DEFAULT_MAX_LIMIT, F as MAX_REGEX_LENGTH, I as MAX_SEARCH_LENGTH, L as MUTATION_OPERATIONS, M as HookOperation, N as HookPhase, O as DEFAULT_TENANT_FIELD, P as MAX_FILTER_DEPTH, R as MutationOperation, S as CRUD_OPERATIONS, T as DEFAULT_LIMIT, f as defineResourceVariants, g as mergeResourceConfig, h as SeamedResourceConfig, j as HOOK_PHASES, k as DEFAULT_UPDATE_METHOD, m as ResourceSeams, p as AdapterLike, s as getControllerScope, v as defineAction, w as DEFAULT_ID_FIELD, x as defineAggregation, z as RESERVED_QUERY_PARAMS } from "./index-B5N-NaHs.mjs";
5
- import { A as requireOwnership, C as createOrgPermissions, D as denyAll, E as anyOf, N as when, S as createDynamicPermissionMatrix, T as allowPublic, b as DynamicPermissionMatrixConfig, c as publicReadAdminWrite, d as requireOrgMembership, f as requireOrgRole, h as requireTeamMembership, i as ownerWithAdminBypass, j as requireRoles, k as requireAuth, l as readOnly, m as requireServiceScope, n as authenticated, o as presets_d_exports, p as requireScopeContext, r as fullPublic, s as publicRead, t as adminOnly, u as requireOrgInScope, w as allOf, y as DynamicPermissionMatrix } from "./index-B5PoNbfu.mjs";
1
+ import { A as RequestIdOptions, B as defineResource, Bt as RouteHandler, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, Fn as ArcUpdateResult, Ft as ControllerLike, H as CrudController, In as BaseControllerOptions, Jt as AnyRecord, L as RequestWithExtras, Ln as ControllerConfigurableOptions, Lt as IController, Mn as ArcDeleteResult, N as FastifyRequestExtras, Nn as ArcGetResult, Nt as RateLimitConfig, O as HealthOptions, P as FastifyWithAuth, Pn as ArcListResult, Q as MiddlewareConfig, Rn as ControllerConstructionOptions, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, U as ResourceConfig, Ut as AuthPluginOptions, V as ResourceDefinition, X as CrudRouteKey, Xt as ArcRequest, Y as PresetResult, Yt as ApiResponse, Z as CrudSchemas, Zt as JWTPayload, a as InferAdapterDoc, an as TreeMixin, c as TypedController, cn as BulkExt, ct as AggMeasureShorthand, d as PaginationResult, dt as AggregationDateRangeRequirement, en as UserOrganization, et as RouteSchemaOptions, f as IntrospectionData, ft as AggregationIndexHint, g as ArcInternalMetadata, gt as AggregationsMap, h as ResourceMetadata, ht as AggregationRateLimit, i as ValidationResult, in as TreeExt, jn as ArcCreateResult, k as IntrospectionPluginOptions, l as TypedRepository, ln as BulkMixin, lt as AggregationCacheConfig, m as RegistryStats, mt as AggregationMaterializedResult, n as ConfigError, nn as SoftDeleteExt, nt as FieldRule, o as InferDocType, on as SlugExt, ot as EventDefinition, p as RegistryEntry, pt as AggregationMaterializedContext, q as PresetFunction, r as ValidateOptions, rn as SoftDeleteMixin, s as InferResourceDoc, sn as SlugMixin, st as AggMeasureInput, t as RouteHandlerMethod, tn as BaseController, u as TypedResourceConfig, un as BaseCrudController, ut as AggregationConfig, w as ServiceContext, y as OwnershipCheck, zn as ListResult, zt as IRequestContext } from "./index-BBsvQt7U.mjs";
2
+ import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-Csxga7d3.mjs";
3
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-D3lhgzhD.mjs";
4
+ import "./fastify-augmentation-g_f9omSc.mjs";
5
+ import { A as HOOK_OPERATIONS, B as SYSTEM_FIELDS, C as CrudOperation, D as DEFAULT_SORT, E as DEFAULT_MAX_LIMIT, F as MAX_REGEX_LENGTH, I as MAX_SEARCH_LENGTH, L as MUTATION_OPERATIONS, M as HookOperation, N as HookPhase, O as DEFAULT_TENANT_FIELD, P as MAX_FILTER_DEPTH, R as MutationOperation, S as CRUD_OPERATIONS, T as DEFAULT_LIMIT, f as defineResourceVariants, g as mergeResourceConfig, h as SeamedResourceConfig, j as HOOK_PHASES, k as DEFAULT_UPDATE_METHOD, m as ResourceSeams, p as AdapterLike, s as getControllerScope, v as defineAction, w as DEFAULT_ID_FIELD, x as defineAggregation, z as RESERVED_QUERY_PARAMS } from "./index-CC5ruDGh.mjs";
6
+ import { A as allOf, E as DynamicPermissionMatrixConfig, F as requireAuth, I as requireOwnership, L as requireRoles, M as anyOf, N as denyAll, O as createDynamicPermissionMatrix, T as DynamicPermissionMatrix, c as publicReadAdminWrite, d as requireOrgMembership, f as requireOrgRole, h as requireTeamMembership, i as ownerWithAdminBypass, j as allowPublic, k as createOrgPermissions, l as readOnly, m as requireServiceScope, n as authenticated, o as presets_d_exports, p as requireScopeContext, r as fullPublic, s as publicRead, t as adminOnly, u as requireOrgInScope, z as when } from "./index-wMoWBLkn.mjs";
6
7
  import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-DHcHVAgk.mjs";
7
8
  //#region src/index.d.ts
8
9
  declare const version: string;
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
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-DNre0wRg.mjs";
2
2
  import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-DkO8Nk6-.mjs";
3
- import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController-BnLYgE9A.mjs";
4
- import { E as requireRoles, F as applyFieldReadPermissions, I as applyFieldWritePermissions, L as fields, O as when, S as denyAll, T as requireOwnership, _ as requireServiceScope, b as allowPublic, c as publicReadAdminWrite, d as createDynamicPermissionMatrix, f as createOrgPermissions, g as requireScopeContext, h as requireOrgRole, i as ownerWithAdminBypass, l as readOnly, m as requireOrgMembership, n as authenticated, o as presets_exports, p as requireOrgInScope, r as fullPublic, s as publicRead, t as adminOnly, v as requireTeamMembership, w as requireAuth, x as anyOf, y as allOf } from "./permissions-BJMuKMl7.mjs";
5
- import { a as mergeResourceConfig, c as defineAction, d as defineAggregation, i as defineResourceVariants, o as defineResource, s as ResourceDefinition } from "./core-DZ6Xe1jR.mjs";
6
- import { l as getControllerScope } from "./actionPermissions-ze70xXJi.mjs";
3
+ import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController-BQ8KNvWC.mjs";
4
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, o as fields } from "./applyPermissionResult-Tw4iXtlf.mjs";
5
+ import { a as defineResource, c as defineAction, d as defineAggregation, i as defineResourceVariants, o as ResourceDefinition, s as mergeResourceConfig } from "./core-DQ6rDTyH.mjs";
6
+ import { C as allowPublic, D as requireAuth, O as requireOwnership, S as allOf, T as denyAll, _ as requireOrgMembership, b as requireServiceScope, c as publicReadAdminWrite, g as requireOrgInScope, h as createOrgPermissions, i as ownerWithAdminBypass, j as when, k as requireRoles, l as readOnly, m as createDynamicPermissionMatrix, n as authenticated, o as presets_exports, r as fullPublic, s as publicRead, t as adminOnly, v as requireOrgRole, w as anyOf, x as requireTeamMembership, y as requireScopeContext } from "./permissions-tbxFHxjZ.mjs";
7
+ import { l as getControllerScope } from "./actionPermissions-lszoJle1.mjs";
7
8
  //#region src/index.ts
8
- const version = "2.21.0";
9
+ const version = "2.23.0";
9
10
  //#endregion
10
11
  export { ArcError, BaseController, BaseCrudController, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, NotFoundError, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, authenticated, createDomainError, createDynamicPermissionMatrix, createOrgPermissions, defineAction, defineAggregation, defineResource, defineResourceVariants, denyAll, fields, fullPublic, getControllerScope, mergeResourceConfig, ownerWithAdminBypass, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, version, when };