@classytic/arc 2.22.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 (86) hide show
  1. package/dist/{BaseController-C6FlRWxF.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 +1 -1
  6. package/dist/auth/index.d.mts +1 -1
  7. package/dist/auth/index.mjs +2 -2
  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-b6fgFgnd.mjs → core-DQ6rDTyH.mjs} +10 -9
  17. package/dist/{createActionRouter-h1cEEaEX.mjs → createActionRouter-BuOv-m4Y.mjs} +3 -3
  18. package/dist/{createAggregationRouter-B1C0kvTL.mjs → createAggregationRouter-CgJv_BU_.mjs} +1 -1
  19. package/dist/{createApp-Ci7yG5gv.mjs → createApp-CjOeWJ9_.mjs} +11 -2
  20. package/dist/docs/index.d.mts +1 -1
  21. package/dist/docs/index.mjs +1 -1
  22. package/dist/{errorHandler-BTzXSwIg.mjs → errorHandler-Bq2OeEQL.mjs} +10 -7
  23. package/dist/events/index.mjs +5 -2
  24. package/dist/factory/index.d.mts +20 -4
  25. package/dist/factory/index.mjs +31 -11
  26. package/dist/{fastify-augmentation-9_fuetB4.d.mts → fastify-augmentation-g_f9omSc.d.mts} +1 -1
  27. package/dist/hooks/index.d.mts +1 -1
  28. package/dist/idempotency/index.d.mts +7 -0
  29. package/dist/idempotency/index.mjs +5 -17
  30. package/dist/{index-DbtgCOCj.d.mts → index-BBsvQt7U.d.mts} +26 -4
  31. package/dist/{index-BEu4wjbL.d.mts → index-Ba10fdKp.d.mts} +11 -2
  32. package/dist/{index-D8MKYGDa.d.mts → index-CC5ruDGh.d.mts} +2 -2
  33. package/dist/{index-ogIHvOjo.d.mts → index-wMoWBLkn.d.mts} +67 -1
  34. package/dist/index.d.mts +4 -4
  35. package/dist/index.mjs +6 -5
  36. package/dist/{init-DleK3p9f.mjs → init-CjO4p_q1.mjs} +5 -2
  37. package/dist/integrations/event-gateway.mjs +1 -1
  38. package/dist/integrations/index.d.mts +1 -1
  39. package/dist/integrations/mcp/index.d.mts +66 -3
  40. package/dist/integrations/mcp/index.mjs +1 -1
  41. package/dist/integrations/mcp/testing.d.mts +1 -1
  42. package/dist/integrations/mcp/testing.mjs +1 -1
  43. package/dist/integrations/webhooks.d.mts +123 -3
  44. package/dist/integrations/webhooks.mjs +160 -5
  45. package/dist/interface-n5hpKg1d.mjs +16 -0
  46. package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
  47. package/dist/{mcpPlugin-ByA2tf6D.mjs → mcpPlugin-BtzIL_kM.mjs} +308 -39
  48. package/dist/middleware/index.d.mts +1 -1
  49. package/dist/{openapi-BRpMZD7C.mjs → openapi-jSDx-VDv.mjs} +3 -3
  50. package/dist/permissions/index.d.mts +2 -2
  51. package/dist/permissions/index.mjs +3 -2
  52. package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
  53. package/dist/pipeline/index.d.mts +1 -1
  54. package/dist/plugins/index.d.mts +6 -6
  55. package/dist/plugins/index.mjs +5 -4
  56. package/dist/plugins/tracing-entry.mjs +1 -1
  57. package/dist/presets/filesUpload.d.mts +1 -1
  58. package/dist/presets/filesUpload.mjs +1 -1
  59. package/dist/presets/index.d.mts +1 -1
  60. package/dist/presets/index.mjs +1 -1
  61. package/dist/presets/multiTenant.d.mts +1 -1
  62. package/dist/presets/search.d.mts +1 -1
  63. package/dist/presets/search.mjs +1 -1
  64. package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
  65. package/dist/realtime-I8OrfnMR.mjs +375 -0
  66. package/dist/{registerRoute-xHc42fwB.mjs → registerRoute-CWO6mdYe.mjs} +1 -1
  67. package/dist/registry/index.d.mts +2 -2
  68. package/dist/registry/index.mjs +1 -1
  69. package/dist/{schemaConverter-Ds5reSGs.mjs → schemaConverter-DJvodiHj.mjs} +4 -1
  70. package/dist/{schemaIR-B-6SPe7g.mjs → schemaIR-Ca7ySNPx.mjs} +1 -1
  71. package/dist/scope/index.mjs +1 -1
  72. package/dist/scope-LONQ3mfZ.mjs +37 -0
  73. package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
  74. package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
  75. package/dist/sseStream-BQ7Qaynp.mjs +57 -0
  76. package/dist/testing/index.d.mts +2 -2
  77. package/dist/testing/index.mjs +2 -2
  78. package/dist/types/index.d.mts +1 -1
  79. package/dist/{types-Bc42CUyk.d.mts → types-5OuqeTDX.d.mts} +37 -2
  80. package/dist/utils/index.d.mts +62 -2
  81. package/dist/utils/index.mjs +5 -3
  82. package/dist/{utils-FhJ88TJE.mjs → utils-CZyeRXTh.mjs} +21 -69
  83. package/dist/{versioning-DLwklPlG.d.mts → versioning-D6PvCwpq.d.mts} +43 -4
  84. package/package.json +64 -4
  85. package/skills/arc/references/production.md +734 -696
  86. package/dist/scope-k15RXfcq.mjs +0 -64
@@ -1,4 +1,4 @@
1
- import { a as edgePreset, c as testingPreset, d as getModuleExports, f as orderModules, h as preloadResourcesAsync, i as developmentPreset, l as workerPreset, m as preloadResources, n as createApp, o as getPreset, p as resolveModule, s as productionPreset, t as ArcFactory, u as defineModule } from "../createApp-Ci7yG5gv.mjs";
1
+ import { a as edgePreset, c as testingPreset, d as getModuleExports, f as orderModules, h as preloadResourcesAsync, i as developmentPreset, l as workerPreset, m as preloadResources, n as createApp, o as getPreset, p as resolveModule, s as productionPreset, t as ArcFactory, u as defineModule } from "../createApp-CjOeWJ9_.mjs";
2
2
  import { t as healthPlugin } from "../health-ScyjIVo5.mjs";
3
3
  import { t as loadResources } from "../loadResources-DpU8pfjV.mjs";
4
4
  //#region src/factory/createWorker.ts
@@ -37,28 +37,37 @@ async function createWorker(options, workerOptions = {}) {
37
37
  * Convert a Fastify app into a Web Standards fetch handler.
38
38
  *
39
39
  * The returned function accepts a Web Standard `Request` and returns
40
- * a Web Standard `Response` — the universal serverless/edge contract.
40
+ * a Web Standard `Response` — the universal serverless contract.
41
41
  *
42
42
  * Internally uses `app.inject()` which processes the request through
43
43
  * the full Fastify pipeline (hooks, plugins, routes) without TCP.
44
+ * Bodies are read as raw bytes (`arrayBuffer()`), so binary payloads
45
+ * (protobuf, images, non-UTF-8 charsets) survive intact — the
46
+ * content-type parser decides how to decode, same as a real socket.
44
47
  */
45
48
  function toFetchHandler(app, options = {}) {
46
- const { autoReady = true } = options;
47
- let ready = false;
49
+ const { autoReady = true, maxRequestBytes = 1048576 } = options;
50
+ let readyPromise = null;
48
51
  return async (request) => {
49
- if (autoReady && !ready) {
50
- await app.ready();
51
- ready = true;
52
+ if (autoReady) {
53
+ readyPromise ??= Promise.resolve(app.ready());
54
+ await readyPromise;
52
55
  }
53
56
  const url = new URL(request.url);
54
57
  const headers = {};
55
58
  request.headers.forEach((value, key) => {
56
59
  headers[key] = value;
57
60
  });
61
+ if (maxRequestBytes > 0) {
62
+ const declared = Number(request.headers.get("content-length"));
63
+ if (Number.isFinite(declared) && declared > maxRequestBytes) return payloadTooLarge(maxRequestBytes);
64
+ }
58
65
  let payload;
59
- if (request.method !== "GET" && request.method !== "HEAD") try {
60
- payload = await request.text();
61
- } catch {}
66
+ if (request.method !== "GET" && request.method !== "HEAD" && request.body !== null) {
67
+ const bytes = await request.arrayBuffer();
68
+ if (maxRequestBytes > 0 && bytes.byteLength > maxRequestBytes) return payloadTooLarge(maxRequestBytes);
69
+ if (bytes.byteLength > 0) payload = Buffer.from(bytes);
70
+ }
62
71
  const response = await app.inject({
63
72
  method: request.method,
64
73
  url: url.pathname + url.search,
@@ -71,11 +80,22 @@ function toFetchHandler(app, options = {}) {
71
80
  if (Array.isArray(value)) for (const v of value) responseHeaders.append(key, v);
72
81
  else responseHeaders.set(key, String(value));
73
82
  }
74
- return new Response(response.payload, {
83
+ return new Response(response.rawPayload, {
75
84
  status: response.statusCode,
76
85
  headers: responseHeaders
77
86
  });
78
87
  };
79
88
  }
89
+ /** Canonical ErrorContract shape for the pre-dispatch 413. */
90
+ function payloadTooLarge(limit) {
91
+ return new Response(JSON.stringify({
92
+ code: "arc.payload_too_large",
93
+ message: `Request body exceeds ${limit} bytes`,
94
+ status: 413
95
+ }), {
96
+ status: 413,
97
+ headers: { "content-type": "application/json" }
98
+ });
99
+ }
80
100
  //#endregion
81
101
  export { ArcFactory, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
@@ -1,4 +1,4 @@
1
- import { bn as HookSystem, z as ResourceRegistry } from "./index-DbtgCOCj.mjs";
1
+ import { xn as HookSystem, z as ResourceRegistry } from "./index-BBsvQt7U.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
3
3
  import { r as ArcModuleRegistry } from "./module-B4u10iOJ.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
@@ -1,2 +1,2 @@
1
- import { Cn as afterDelete, Dn as beforeUpdate, En as beforeDelete, On as createHookSystem, Sn as afterCreate, Tn as beforeCreate, _n as HookOperation, bn as HookSystem, gn as HookHandler, hn as HookContext, kn as defineHook, mn as DefineHookOptions, vn as HookPhase, wn as afterUpdate, xn as HookSystemOptions, yn as HookRegistration } from "../index-DbtgCOCj.mjs";
1
+ import { An as defineHook, Cn as afterCreate, Dn as beforeDelete, En as beforeCreate, On as beforeUpdate, Sn as HookSystemOptions, Tn as afterUpdate, _n as HookHandler, bn as HookRegistration, gn as HookContext, hn as DefineHookOptions, kn as createHookSystem, vn as HookOperation, wn as afterDelete, xn as HookSystem, yn as HookPhase } from "../index-BBsvQt7U.mjs";
2
2
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -77,6 +77,13 @@ declare module "fastify" {
77
77
  * ```
78
78
  */
79
79
  middleware: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
80
+ /**
81
+ * The resolved backing store. Exposed so non-HTTP executors (MCP
82
+ * tools via `mcpPlugin`'s execution wiring, jobs, workflows) can run
83
+ * the same check → lock → execute → record protocol against the same
84
+ * backend. `undefined` when the plugin is disabled.
85
+ */
86
+ store?: IdempotencyStore;
80
87
  };
81
88
  }
82
89
  }
@@ -1,4 +1,5 @@
1
1
  import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DY6xq4sM.mjs";
2
+ import { t as createIdempotencyResult } from "../interface-n5hpKg1d.mjs";
2
3
  import { createHash } from "node:crypto";
3
4
  import fp from "fastify-plugin";
4
5
  import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
@@ -100,21 +101,6 @@ function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
100
101
  };
101
102
  }
102
103
  //#endregion
103
- //#region src/idempotency/stores/interface.ts
104
- /**
105
- * Helper to create a result object
106
- */
107
- function createIdempotencyResult(statusCode, body, headers, ttlMs) {
108
- const now = /* @__PURE__ */ new Date();
109
- return {
110
- statusCode,
111
- headers,
112
- body,
113
- createdAt: now,
114
- expiresAt: new Date(now.getTime() + ttlMs)
115
- };
116
- }
117
- //#endregion
118
104
  //#region src/idempotency/stores/memory.ts
119
105
  var MemoryIdempotencyStore = class {
120
106
  name = "memory";
@@ -283,7 +269,8 @@ const idempotencyPlugin = async (fastify, opts = {}) => {
283
269
  fastify.decorate("idempotency", {
284
270
  invalidate: async () => {},
285
271
  has: async () => false,
286
- middleware: async () => {}
272
+ middleware: async () => {},
273
+ store: void 0
287
274
  });
288
275
  fastify.decorateRequest("idempotencyKey", void 0);
289
276
  fastify.decorateRequest("idempotencyReplayed", false);
@@ -380,7 +367,8 @@ const idempotencyPlugin = async (fastify, opts = {}) => {
380
367
  has: async (key) => {
381
368
  return !!await store.findByPrefix(`${key}:`);
382
369
  },
383
- middleware: idempotencyMiddleware
370
+ middleware: idempotencyMiddleware,
371
+ store
384
372
  });
385
373
  fastify.addHook("preSerialization", async (request, reply, payload) => {
386
374
  if (request.idempotencyReplayed) return payload;
@@ -1097,8 +1097,14 @@ interface JWTPayload {
1097
1097
  [key: string]: unknown;
1098
1098
  }
1099
1099
  /**
1100
- * Standard API response envelope — `{ success, data?, error?, message?, meta? }`.
1101
- * Used by Arc's default response shape.
1100
+ * @deprecated LEGACY envelope — arc's actual wire contract has not used
1101
+ * `{ success, data, error, message }` for a long time. Successes return
1102
+ * DIRECT data (see `IControllerResponse` — `{ data, meta?, status?, headers? }`
1103
+ * flattened onto the wire), and errors return the canonical `ErrorContract`
1104
+ * (`{ code, message, status, details? }` from `@classytic/repo-core/errors`)
1105
+ * emitted by the global error handler. Keeping this type visible teaches new
1106
+ * applications and AI agents to generate an obsolete shape. Scheduled for
1107
+ * removal in v3. Do NOT build new response handling against it.
1102
1108
  */
1103
1109
  interface ApiResponse<T = unknown> {
1104
1110
  success: boolean;
@@ -1423,7 +1429,15 @@ interface IRequestContext<TBody = unknown, TParams extends Record<string, string
1423
1429
  body: TBody;
1424
1430
  /** Authenticated user or null */
1425
1431
  user: TUser | null;
1426
- /** Request headers */
1432
+ /**
1433
+ * Request headers.
1434
+ *
1435
+ * ⚠ Declared narrow for ergonomics, but on the HTTP path this is
1436
+ * Fastify's headers object passed through — repeated headers arrive as
1437
+ * `string[]` at RUNTIME behind this type. Read via `getHeader(ctx.headers,
1438
+ * name)` from `@classytic/arc/utils` for array-safe, duplicate-deterministic
1439
+ * access. The declared type widens to `ArcHeaders` in v3.
1440
+ */
1427
1441
  headers: Record<string, string | undefined>;
1428
1442
  /** Organization ID (for multi-tenant apps) */
1429
1443
  organizationId?: string;
@@ -4055,6 +4069,14 @@ interface RegistryEntry extends ResourceMetadata {
4055
4069
  roles?: readonly string[];
4056
4070
  redactValue?: unknown;
4057
4071
  }>;
4072
+ /**
4073
+ * The LIVE field-permission map (2.22) — the same object `defineResource`
4074
+ * consumes, surfaced so request-time consumers (the realtime change feed)
4075
+ * can apply `applyFieldReadPermissions` masking to event payloads without
4076
+ * re-reading the resource definition. `fieldPermissions` above stays the
4077
+ * introspection-safe metadata twin.
4078
+ */
4079
+ fields?: FieldPermissionMap;
4058
4080
  /** Pipeline step names (for OpenAPI data) */
4059
4081
  pipelineSteps?: Array<{
4060
4082
  type: string;
@@ -4237,4 +4259,4 @@ interface ValidateOptions {
4237
4259
  strict?: boolean;
4238
4260
  }
4239
4261
  //#endregion
4240
- export { OpenApiSchemas as $, UserOrganization as $t, RequestIdOptions as A, ArcCreateResult as An, RouteDefinition as At, defineResource as B, BodySanitizerConfig as Bn, RouteHandler as Bt, RequestContext as C, afterDelete as Cn, PipelineStep as Ct, HealthCheck as D, beforeUpdate as Dn, ActionEntry as Dt, GracefulShutdownOptions as E, beforeDelete as En, ActionDefinition as Et, FastifyWithDecorators as F, BaseControllerOptions as Fn, ControllerLike as Ft, OnTenantDeleteConfig as G, JwtContext as Gt, CrudController as H, AuthPluginOptions as Ht, MiddlewareHandler as I, ControllerConfigurableOptions as In, FastifyHandler as It, PresetHook as J, ApiResponse as Jt, ResolvedTenantPurge as K, TokenPair as Kt, RequestWithExtras as L, ControllerConstructionOptions as Ln, IController as Lt, EventsDecorator as M, ArcGetResult as Mn, RouteMethod as Mt, FastifyRequestExtras as N, ArcListResult as Nn, RateLimitConfig as Nt, HealthOptions as O, createHookSystem as On, ActionHandlerFn as Ot, FastifyWithAuth as P, ArcUpdateResult as Pn, ControllerHandler as Pt, MiddlewareConfig as Q, UserLike as Qt, RegisterOptions as R, ListResult as Rn, IControllerResponse as Rt, QueryParserInterface as S, afterCreate as Sn, PipelineContext as St, CrudRouterOptions as T, beforeCreate as Tn, ResourceCacheConfig as Tt, ResourceConfig as U, Authenticator as Ut, ResourceDefinition as V, AuthHelpers as Vt, ResourcePermissions as W, AuthenticatorContext as Wt, CrudRouteKey as X, JWTPayload as Xt, PresetResult as Y, ArcRequest as Yt, CrudSchemas as Z, ObjectId as Zt, ControllerQueryOptions as _, HookOperation as _n, Guard as _t, InferAdapterDoc as a, SlugExt as an, ResourceExtensions as at, ParsedQuery as b, HookSystem as bn, OperationFilter as bt, TypedController as c, BulkMixin as cn, AggMeasureShorthand as ct, PaginationResult as d, QueryResolverConfig as dn, AggregationDateRangeRequirement as dt, BaseController as en, RouteSchemaOptions as et, IntrospectionData as f, AccessControl as fn, AggregationIndexHint as ft, ArcInternalMetadata as g, HookHandler as gn, AggregationsMap as gt, ResourceMetadata as h, HookContext as hn, AggregationRateLimit as ht, ValidationResult as i, TreeMixin as in, ResourceHooks as it, ArcDecorator as j, ArcDeleteResult as jn, RouteMcpConfig as jt, IntrospectionPluginOptions as k, defineHook as kn, ActionsMap as kt, TypedRepository as l, BaseCrudController as ln, AggregationCacheConfig as lt, RegistryStats as m, DefineHookOptions as mn, AggregationMaterializedResult as mt, ConfigError as n, SoftDeleteMixin as nn, FieldRule$1 as nt, InferDocType as o, SlugMixin as on, EventDefinition as ot, RegistryEntry as p, AccessControlConfig as pn, AggregationMaterializedContext as pt, PresetFunction as q, AnyRecord as qt, ValidateOptions as r, TreeExt as rn, ResourceHookContext as rt, InferResourceDoc as s, BulkExt as sn, AggMeasureInput as st, RouteHandlerMethod$1 as t, SoftDeleteExt as tn, ArcFieldRule as tt, TypedResourceConfig as u, QueryResolver as un, AggregationConfig as ut, LookupOption as v, HookPhase as vn, Interceptor as vt, ServiceContext as w, afterUpdate as wn, Transform as wt, PopulateOption as x, HookSystemOptions as xn, PipelineConfig as xt, OwnershipCheck as y, HookRegistration as yn, NextFunction as yt, ResourceRegistry as z, BodySanitizer as zn, IRequestContext as zt };
4262
+ export { OpenApiSchemas as $, UserLike as $t, RequestIdOptions as A, defineHook as An, RouteDefinition as At, defineResource as B, BodySanitizer as Bn, RouteHandler as Bt, RequestContext as C, afterCreate as Cn, PipelineStep as Ct, HealthCheck as D, beforeDelete as Dn, ActionEntry as Dt, GracefulShutdownOptions as E, beforeCreate as En, ActionDefinition as Et, FastifyWithDecorators as F, ArcUpdateResult as Fn, ControllerLike as Ft, OnTenantDeleteConfig as G, AuthenticatorContext as Gt, CrudController as H, AuthHelpers as Ht, MiddlewareHandler as I, BaseControllerOptions as In, FastifyHandler as It, PresetHook as J, AnyRecord as Jt, ResolvedTenantPurge as K, JwtContext as Kt, RequestWithExtras as L, ControllerConfigurableOptions as Ln, IController as Lt, EventsDecorator as M, ArcDeleteResult as Mn, RouteMethod as Mt, FastifyRequestExtras as N, ArcGetResult as Nn, RateLimitConfig as Nt, HealthOptions as O, beforeUpdate as On, ActionHandlerFn as Ot, FastifyWithAuth as P, ArcListResult as Pn, ControllerHandler as Pt, MiddlewareConfig as Q, ObjectId as Qt, RegisterOptions as R, ControllerConstructionOptions as Rn, IControllerResponse as Rt, QueryParserInterface as S, HookSystemOptions as Sn, PipelineContext as St, CrudRouterOptions as T, afterUpdate as Tn, ResourceCacheConfig as Tt, ResourceConfig as U, AuthPluginOptions as Ut, ResourceDefinition as V, BodySanitizerConfig as Vn, ServerAccessor as Vt, ResourcePermissions as W, Authenticator as Wt, CrudRouteKey as X, ArcRequest as Xt, PresetResult as Y, ApiResponse as Yt, CrudSchemas as Z, JWTPayload as Zt, ControllerQueryOptions as _, HookHandler as _n, Guard as _t, InferAdapterDoc as a, TreeMixin as an, ResourceExtensions as at, ParsedQuery as b, HookRegistration as bn, OperationFilter as bt, TypedController as c, BulkExt as cn, AggMeasureShorthand as ct, PaginationResult as d, QueryResolver as dn, AggregationDateRangeRequirement as dt, UserOrganization as en, RouteSchemaOptions as et, IntrospectionData as f, QueryResolverConfig as fn, AggregationIndexHint as ft, ArcInternalMetadata as g, HookContext as gn, AggregationsMap as gt, ResourceMetadata as h, DefineHookOptions as hn, AggregationRateLimit as ht, ValidationResult as i, TreeExt as in, ResourceHooks as it, ArcDecorator as j, ArcCreateResult as jn, RouteMcpConfig as jt, IntrospectionPluginOptions as k, createHookSystem as kn, ActionsMap as kt, TypedRepository as l, BulkMixin as ln, AggregationCacheConfig as lt, RegistryStats as m, AccessControlConfig as mn, AggregationMaterializedResult as mt, ConfigError as n, SoftDeleteExt as nn, FieldRule$1 as nt, InferDocType as o, SlugExt as on, EventDefinition as ot, RegistryEntry as p, AccessControl as pn, AggregationMaterializedContext as pt, PresetFunction as q, TokenPair as qt, ValidateOptions as r, SoftDeleteMixin as rn, ResourceHookContext as rt, InferResourceDoc as s, SlugMixin as sn, AggMeasureInput as st, RouteHandlerMethod$1 as t, BaseController as tn, ArcFieldRule as tt, TypedResourceConfig as u, BaseCrudController as un, AggregationConfig as ut, LookupOption as v, HookOperation as vn, Interceptor as vt, ServiceContext as w, afterDelete as wn, Transform as wt, PopulateOption as x, HookSystem as xn, PipelineConfig as xt, OwnershipCheck as y, HookPhase as yn, NextFunction as yt, ResourceRegistry as z, ListResult as zn, IRequestContext as zt };
@@ -1,4 +1,4 @@
1
- import { Ut as Authenticator } from "./index-DbtgCOCj.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";
@@ -7,7 +7,7 @@ import { r as QueryCachePluginOptions } from "./queryCachePlugin-CfYWq-Ak.mjs";
7
7
  import { t as ErrorHandlerOptions } from "./errorHandler-C6yv4ZTW.mjs";
8
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-DLwklPlG.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
@@ -672,6 +672,15 @@ interface CreateAppOptions {
672
672
  * Requires emitEvents to be enabled (or events plugin registered).
673
673
  */
674
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;
675
684
  /**
676
685
  * QueryCache — TanStack Query-inspired server cache with SWR.
677
686
  * Default: false (opt-in). Set to true for memory store defaults.
@@ -1,7 +1,7 @@
1
- import { C as RequestContext, Et as ActionDefinition, F as FastifyWithDecorators, H as CrudController, L as RequestWithExtras, Lt as IController, Rt as IControllerResponse, T as CrudRouterOptions, U as ResourceConfig, V as ResourceDefinition, et as RouteSchemaOptions, jt as RouteMcpConfig, qt as AnyRecord, tt as ArcFieldRule, ut as AggregationConfig, zt as IRequestContext } from "./index-DbtgCOCj.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
3
  import { t as PermissionCheck } from "./types-Csxga7d3.mjs";
4
- import "./fastify-augmentation-9_fuetB4.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";
@@ -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,9 +1,9 @@
1
- import { $t as UserOrganization, A as RequestIdOptions, An as ArcCreateResult, B as defineResource, Bt as RouteHandler, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, Fn as BaseControllerOptions, Ft as ControllerLike, H as CrudController, Ht as AuthPluginOptions, In as ControllerConfigurableOptions, Jt as ApiResponse, L as RequestWithExtras, Ln as ControllerConstructionOptions, Lt as IController, Mn as ArcGetResult, N as FastifyRequestExtras, Nn as ArcListResult, Nt as RateLimitConfig, O as HealthOptions, P as FastifyWithAuth, Pn as ArcUpdateResult, Q as MiddlewareConfig, Rn as ListResult, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, U as ResourceConfig, V as ResourceDefinition, X as CrudRouteKey, Xt as JWTPayload, Y as PresetResult, Yt as ArcRequest, Z as CrudSchemas, a as InferAdapterDoc, an as SlugExt, c as TypedController, cn as BulkMixin, ct as AggMeasureShorthand, d as PaginationResult, dt as AggregationDateRangeRequirement, en as BaseController, 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 TreeMixin, jn as ArcDeleteResult, k as IntrospectionPluginOptions, l as TypedRepository, ln as BaseCrudController, lt as AggregationCacheConfig, m as RegistryStats, mt as AggregationMaterializedResult, n as ConfigError, nn as SoftDeleteMixin, nt as FieldRule, o as InferDocType, on as SlugMixin, ot as EventDefinition, p as RegistryEntry, pt as AggregationMaterializedContext, q as PresetFunction, qt as AnyRecord, r as ValidateOptions, rn as TreeExt, s as InferResourceDoc, sn as BulkExt, st as AggMeasureInput, t as RouteHandlerMethod, tn as SoftDeleteExt, u as TypedResourceConfig, ut as AggregationConfig, w as ServiceContext, y as OwnershipCheck, zt as IRequestContext } from "./index-DbtgCOCj.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
2
  import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-Csxga7d3.mjs";
3
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-9_fuetB4.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-D8MKYGDa.mjs";
6
- 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-ogIHvOjo.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";
7
7
  import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-DHcHVAgk.mjs";
8
8
  //#region src/index.d.ts
9
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-C6FlRWxF.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 defineResource, c as defineAction, d as defineAggregation, i as defineResourceVariants, o as ResourceDefinition, s as mergeResourceConfig } from "./core-b6fgFgnd.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.22.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 };
@@ -1074,10 +1074,10 @@ const SCAFFOLD_DEP_VERSIONS = {
1074
1074
  },
1075
1075
  devCommon: {
1076
1076
  "@biomejs/biome": "^2.4.15",
1077
- "mongodb-memory-server": "^11.1.0",
1078
1077
  "pino-pretty": "^13.1.3",
1079
1078
  vitest: "^4.1.7"
1080
1079
  },
1080
+ devMongokit: { "mongodb-memory-server": "^11.1.0" },
1081
1081
  devTypescript: {
1082
1082
  "@types/node": "^22.10.0",
1083
1083
  tsx: "^4.22.3",
@@ -1205,7 +1205,10 @@ function resolveScaffoldDependencies(config) {
1205
1205
  Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.authJwt);
1206
1206
  if (config.typescript) Object.assign(devDependencies, SCAFFOLD_DEP_VERSIONS.typesJwt);
1207
1207
  }
1208
- if (config.adapter === "mongokit") Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.adapterMongokit);
1208
+ if (config.adapter === "mongokit") {
1209
+ Object.assign(dependencies, SCAFFOLD_DEP_VERSIONS.adapterMongokit);
1210
+ Object.assign(devDependencies, SCAFFOLD_DEP_VERSIONS.devMongokit);
1211
+ }
1209
1212
  if (config.typescript) Object.assign(devDependencies, SCAFFOLD_DEP_VERSIONS.devTypescript);
1210
1213
  applyLatestOverlay(dependencies);
1211
1214
  applyLatestOverlay(devDependencies);
@@ -4,7 +4,7 @@ const eventGatewayPluginImpl = async (fastify, opts = {}) => {
4
4
  const { auth = true, orgScoped = false, roomPolicy, maxMessageBytes, maxSubscriptionsPerClient, authenticate } = opts;
5
5
  if (auth && !authenticate && !fastify.hasDecorator("authenticate")) throw new Error("[arc-event-gateway] auth is true but fastify.authenticate is not registered. Register an auth plugin first, provide a custom authenticate function, or set auth: false.");
6
6
  if (opts.sse !== false) {
7
- const { default: ssePlugin } = await import("../sse-CfUjwjDk.mjs").then((n) => n.r);
7
+ const { default: ssePlugin } = await import("../sse-0u4P4-OI.mjs").then((n) => n.r);
8
8
  await fastify.register(ssePlugin, {
9
9
  path: opts.sse?.path ?? "/events/stream",
10
10
  requireAuth: auth,
@@ -1,7 +1,7 @@
1
1
  import { c as WebSocketClient, l as WebSocketMessage, u as WebSocketPluginOptions } from "../websocket-uuWqM6UY.mjs";
2
2
  import { EventGatewayOptions } from "./event-gateway.mjs";
3
3
  import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
4
- import { c as McpAuthResult, d as PromptDefinition, g as ToolDefinition, h as ToolContext, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpResourceConfig } from "../types-Bc42CUyk.mjs";
4
+ import { _ as ToolDefinition, c as McpAuthResult, d as McpResourceConfig, f as PromptDefinition, g as ToolContext, h as ToolAnnotations, n as CallToolResult, o as CrudOperation, r as CreateMcpServerConfig, t as BetterAuthHandler, u as McpPluginOptions } from "../types-5OuqeTDX.mjs";
5
5
  import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
6
6
  import { WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription } from "./webhooks.mjs";
7
7
  export type { BetterAuthHandler, CallToolResult, CreateMcpServerConfig, CrudOperation, EventGatewayOptions, JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, McpAuthResult, McpPluginOptions, McpResourceConfig, PromptDefinition, QueueStats, StreamlinePluginOptions, ToolAnnotations, ToolContext, ToolDefinition, WebSocketClient, WebSocketMessage, WebSocketPluginOptions, WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription, WorkflowLike, WorkflowRunLike };
@@ -1,7 +1,8 @@
1
- import { Rt as IControllerResponse, V as ResourceDefinition, zt as IRequestContext } from "../../index-DbtgCOCj.mjs";
1
+ import { Rt as IControllerResponse, V as ResourceDefinition, Vt as ServerAccessor, zt as IRequestContext } from "../../index-BBsvQt7U.mjs";
2
2
  import { i as RequestScope } from "../../types-DH0GupI7.mjs";
3
3
  import { t as PermissionCheck } from "../../types-Csxga7d3.mjs";
4
- import { a as CrudDescriptionOverride, c as McpAuthResult, d as PromptDefinition, f as PromptResult, g as ToolDefinition, h as ToolContext, i as CrudDescriptionMeta, l as McpPluginOptions, m as ToolAnnotations, n as CallToolResult, o as CrudOperation, p as SessionEntry, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpResourceConfig } from "../../types-Bc42CUyk.mjs";
4
+ import { n as FieldPermissionMap } from "../../fields-D3lhgzhD.mjs";
5
+ import { _ as ToolDefinition, a as CrudDescriptionOverride, c as McpAuthResult, d as McpResourceConfig, f as PromptDefinition, g as ToolContext, h as ToolAnnotations, i as CrudDescriptionMeta, l as McpExecutionWiring, m as SessionEntry, n as CallToolResult, o as CrudOperation, p as PromptResult, r as CreateMcpServerConfig, s as McpAuthResolver, t as BetterAuthHandler, u as McpPluginOptions } from "../../types-5OuqeTDX.mjs";
5
6
  import { ErrorContract } from "@classytic/repo-core/errors";
6
7
  import { FastifyPluginAsync } from "fastify";
7
8
  import { z } from "zod";
@@ -299,6 +300,34 @@ declare function customGuard(check: (ctx: ToolContext) => boolean | Promise<bool
299
300
  //#endregion
300
301
  //#region src/integrations/mcp/buildRequestContext.d.ts
301
302
  type McpOperation = "list" | "get" | "create" | "update" | "delete" | "action";
303
+ /**
304
+ * Optional context extras — the pieces of `IRequestContext` that HTTP's
305
+ * `createRequestContext` assembles from Fastify decorators and the MCP
306
+ * synthetic path historically DROPPED. Without them, controller code that
307
+ * reads `metadata.arc.hooks` (the whole hook system — including the
308
+ * arcCorePlugin after-hook that publishes `<resource>.<op>d` events) or
309
+ * `ctx.server.events` silently no-ops on MCP calls: agents could mutate
310
+ * records without hooks firing, events publishing, realtime feeds updating,
311
+ * or webhooks dispatching. Thread these through `invokeController` /
312
+ * `resourceToTools({ wiring })` to restore full HTTP ↔ MCP parity.
313
+ */
314
+ interface McpContextExtras {
315
+ /**
316
+ * Resource wiring — becomes `metadata.arc`, the same frozen shape
317
+ * `buildArcDecorator` stamps on HTTP requests (`ArcRouteMeta`).
318
+ */
319
+ arc?: {
320
+ readonly resourceName: string;
321
+ readonly schemaOptions?: unknown;
322
+ readonly permissions?: unknown;
323
+ readonly hooks?: unknown;
324
+ readonly events?: unknown;
325
+ readonly fields?: unknown;
326
+ readonly idField?: string;
327
+ };
328
+ /** Server accessor — `ctx.server.events/audit/log`, mirroring HTTP. */
329
+ server?: ServerAccessor;
330
+ }
302
331
  /**
303
332
  * Build an IRequestContext from MCP tool input and session auth.
304
333
  *
@@ -317,7 +346,7 @@ type McpOperation = "list" | "get" | "create" | "update" | "delete" | "action";
317
346
  * the session-derived scope is `public` (i.e. MCP called with `auth: false`).
318
347
  * An authenticated session scope is never overwritten.
319
348
  */
320
- declare function buildRequestContext(input: Record<string, unknown>, auth: McpAuthResult | null, operation: McpOperation, policyFilters?: Record<string, unknown>, scopeOverride?: RequestScope): IRequestContext;
349
+ declare function buildRequestContext(input: Record<string, unknown>, auth: McpAuthResult | null, operation: McpOperation, policyFilters?: Record<string, unknown>, scopeOverride?: RequestScope, extras?: McpContextExtras): IRequestContext;
321
350
  //#endregion
322
351
  //#region src/integrations/mcp/invokeController.d.ts
323
352
  /**
@@ -349,6 +378,33 @@ interface InvokeControllerOptions {
349
378
  * action handler reached through a `string` route handler).
350
379
  */
351
380
  readonly methodName?: string;
381
+ /**
382
+ * Resource field-permission map (`ResourceConfig.fields`). When present,
383
+ * field-level READ permissions are applied to the response payload before
384
+ * serialization — the same masking the HTTP wire and realtime frames get.
385
+ * Omit only for tools whose output carries no resource documents.
386
+ */
387
+ readonly fields?: FieldPermissionMap;
388
+ /**
389
+ * App-level execution wiring (hooks system, event bus, audit, logger,
390
+ * idempotency store). When present, the synthetic request context carries
391
+ * `metadata.arc` + `server` exactly like HTTP's `createRequestContext` —
392
+ * so resource hooks run, CRUD events publish, and `ctx.server.*` works.
393
+ * `mcpPlugin` supplies this automatically; omit for dispatch-only calls.
394
+ */
395
+ readonly wiring?: McpExecutionWiring;
396
+ /** Resource `schemaOptions` — rides on `metadata.arc` like HTTP. */
397
+ readonly schemaOptions?: unknown;
398
+ /** Resource `idField` — rides on `metadata.arc` for `getEntityIdField()`. */
399
+ readonly idField?: string;
400
+ /**
401
+ * Caller-supplied idempotency key (mutations only). Requires
402
+ * `wiring.idempotencyStore`; the executor then runs the same
403
+ * check → lock → execute → record → replay protocol the HTTP
404
+ * idempotency plugin runs, keyed by tool + input + caller identity.
405
+ * A concurrent call with the same key gets a 409 `arc.idempotency_conflict`.
406
+ */
407
+ readonly idempotencyKey?: string;
352
408
  }
353
409
  /**
354
410
  * Invoke a controller method through the MCP synthetic-context path.
@@ -549,6 +605,13 @@ interface ResourceToToolsConfig extends McpResourceConfig {
549
605
  toolNamePrefix?: string;
550
606
  /** Per-operation tool name overrides: `{ get: 'get_job_by_id' }` */
551
607
  names?: Partial<Record<CrudOperation, string>>;
608
+ /**
609
+ * App-level execution wiring (hooks, events, audit, log, idempotency
610
+ * store). `mcpPlugin` supplies this automatically from its Fastify
611
+ * instance; standalone callers pass their own or omit it for
612
+ * dispatch-only tools (no hooks/events/idempotency).
613
+ */
614
+ wiring?: McpExecutionWiring;
552
615
  }
553
616
  /**
554
617
  * Convert a ResourceDefinition into MCP ToolDefinitions.
@@ -1,4 +1,4 @@
1
- import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-ByA2tf6D.mjs";
1
+ import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-BtzIL_kM.mjs";
2
2
  //#region src/integrations/mcp/defineTool.ts
3
3
  /**
4
4
  * Define a type-safe MCP tool.
@@ -1,4 +1,4 @@
1
- import { c as McpAuthResult, l as McpPluginOptions } from "../../types-Bc42CUyk.mjs";
1
+ import { c as McpAuthResult, u as McpPluginOptions } from "../../types-5OuqeTDX.mjs";
2
2
  //#region src/integrations/mcp/testing.d.ts
3
3
  interface TestMcpClientOptions {
4
4
  /** MCP plugin options (resources, overrides, etc.) — same as mcpPlugin config */
@@ -1,4 +1,4 @@
1
- import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-ByA2tf6D.mjs";
1
+ import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-BtzIL_kM.mjs";
2
2
  //#region src/integrations/mcp/testing.ts
3
3
  /**
4
4
  * @classytic/arc/mcp/testing — MCP Test Utilities