@classytic/arc 2.18.5 → 2.20.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 (92) hide show
  1. package/README.md +1 -1
  2. package/bin/arc.js +37 -0
  3. package/dist/{BaseController-Dy6EQHjM.mjs → BaseController-D-MHZ27_.mjs} +4 -4
  4. package/dist/{ResourceRegistry-5fKZwhK_.mjs → ResourceRegistry-7RnrEWNP.mjs} +1 -1
  5. package/dist/{actionPermissions-h7DYDNMg.mjs → actionPermissions-C9RMiIpc.mjs} +2 -1
  6. package/dist/audit/index.mjs +1 -1
  7. package/dist/auth/index.d.mts +1 -1
  8. package/dist/auth/index.mjs +17 -6
  9. package/dist/{buildHandler-B0qvAguf.mjs → buildHandler-D_2ULWxZ.mjs} +3 -3
  10. package/dist/cache/index.mjs +2 -2
  11. package/dist/cli/commands/describe.d.mts +1 -1
  12. package/dist/cli/commands/describe.mjs +2 -2
  13. package/dist/cli/commands/docs.mjs +2 -2
  14. package/dist/cli/commands/generate.mjs +1 -1
  15. package/dist/cli/commands/init.mjs +1 -1
  16. package/dist/cli/commands/introspect.mjs +4 -13
  17. package/dist/cli/index.mjs +2 -2
  18. package/dist/{constants-gaEeMlz-.mjs → constants-DNre0wRg.mjs} +1 -1
  19. package/dist/core/index.d.mts +2 -2
  20. package/dist/core/index.mjs +4 -4
  21. package/dist/{core-ZnQBa5rh.mjs → core-CWhyGkiM.mjs} +136 -59
  22. package/dist/{createActionRouter-CagItpxI.mjs → createActionRouter-HlQso0jO.mjs} +8 -7
  23. package/dist/{createAggregationRouter-DVKroXhC.mjs → createAggregationRouter-5LzPzgw1.mjs} +5 -4
  24. package/dist/{createApp--703hQE1.mjs → createApp-D6uwzCKu.mjs} +210 -17
  25. package/dist/docs/index.d.mts +1 -1
  26. package/dist/docs/index.mjs +1 -1
  27. package/dist/encryption/index.d.mts +222 -0
  28. package/dist/encryption/index.mjs +270 -0
  29. package/dist/events/index.mjs +9 -6
  30. package/dist/factory/index.d.mts +3 -2
  31. package/dist/factory/index.mjs +2 -2
  32. package/dist/{fastify-augmentation-Cxb0ROIw.d.mts → fastify-augmentation-CCQJEDgh.d.mts} +12 -1
  33. package/dist/{generate-Brans3fW.mjs → generate-usRSdQmP.mjs} +4 -3
  34. package/dist/hooks/index.d.mts +1 -1
  35. package/dist/idempotency/index.mjs +1 -1
  36. package/dist/{index-CRKiiP5o.d.mts → index-CdFkIMEd.d.mts} +5 -0
  37. package/dist/{index-Dh0_RSTd.d.mts → index-DUa8WHz3.d.mts} +7 -7
  38. package/dist/{index-CHNQIhcb.d.mts → index-_--GyzcO.d.mts} +111 -2
  39. package/dist/index.d.mts +3 -3
  40. package/dist/index.mjs +6 -6
  41. package/dist/{init-Bqpt1Hxf.mjs → init-YH_nslQR.mjs} +87 -5
  42. package/dist/integrations/index.d.mts +1 -1
  43. package/dist/integrations/mcp/index.d.mts +2 -2
  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/streamline.d.mts +17 -5
  48. package/dist/integrations/streamline.mjs +9 -5
  49. package/dist/{loadResourcesFromEntry-BTlFEx05.mjs → loadResourcesFromEntry-OMCvIEE7.mjs} +1 -1
  50. package/dist/{mcpPlugin-D1wuRD2c.mjs → mcpPlugin-CuW6L_uD.mjs} +148 -49
  51. package/dist/middleware/index.d.mts +1 -1
  52. package/dist/middleware/index.mjs +1 -1
  53. package/dist/module-JYH9V0I8.d.mts +384 -0
  54. package/dist/{openapi-xVJibapr.mjs → openapi-Dq6RbEGK.mjs} +3 -3
  55. package/dist/permissions/index.d.mts +1 -1
  56. package/dist/permissions/index.mjs +1 -1
  57. package/dist/{permissions-BPB1xqGf.mjs → permissions-CgbOLJZG.mjs} +10 -2
  58. package/dist/pipeline/index.d.mts +1 -1
  59. package/dist/plugins/index.d.mts +10 -5
  60. package/dist/plugins/index.mjs +3 -3
  61. package/dist/plugins/tracing-entry.mjs +1 -1
  62. package/dist/{pluralize-DccFV9R7.mjs → pluralize-BhrgwZb9.mjs} +3 -3
  63. package/dist/presets/filesUpload.d.mts +1 -1
  64. package/dist/presets/filesUpload.mjs +1 -1
  65. package/dist/presets/index.d.mts +16 -2
  66. package/dist/presets/index.mjs +1 -1
  67. package/dist/presets/multiTenant.d.mts +1 -1
  68. package/dist/presets/multiTenant.mjs +1 -1
  69. package/dist/presets/search.d.mts +1 -1
  70. package/dist/presets/search.mjs +3 -2
  71. package/dist/{presets-BtyFwTO4.mjs → presets-DWsYQdbV.mjs} +1 -1
  72. package/dist/{queryCachePlugin-emHb0CuX.mjs → queryCachePlugin-DzqLN904.mjs} +2 -2
  73. package/dist/{registerRoute-CPmdkZGu.mjs → registerRoute-DGSREwPw.mjs} +20 -2
  74. package/dist/registry/index.d.mts +1 -1
  75. package/dist/registry/index.mjs +2 -2
  76. package/dist/{registry-Bm9LjWKs.mjs → registry-vcOCtPij.mjs} +1 -1
  77. package/dist/{schemaIR-D1MJwqmp.mjs → schemaIR-ajetw3Fv.mjs} +1 -42
  78. package/dist/scim/index.mjs +2 -2
  79. package/dist/sync/changelog.d.mts +2 -0
  80. package/dist/sync/changelog.mjs +2 -0
  81. package/dist/testing/index.d.mts +2 -2
  82. package/dist/testing/index.mjs +2 -2
  83. package/dist/types/index.d.mts +2 -2
  84. package/dist/{types-CmkknLzI.d.mts → types-BHMKlMBU.d.mts} +53 -195
  85. package/dist/{types-D60PFwfG.d.mts → types-C9b2HR5W.d.mts} +1 -1
  86. package/dist/utils/index.d.mts +1 -1
  87. package/dist/utils/index.mjs +1 -1
  88. package/dist/{utils-C6QQ9-hd.mjs → utils-ChjYQpjn.mjs} +1400 -1400
  89. package/dist/{validate-CRcaWzp2.mjs → validate-C2mGgSXg.mjs} +3 -3
  90. package/package.json +22 -8
  91. package/skills/arc/SKILL.md +10 -0
  92. package/skills/arc/references/mcp.md +2 -0
@@ -0,0 +1,270 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2
+ import fp from "fastify-plugin";
3
+ //#region src/encryption/fieldCipher.ts
4
+ /**
5
+ * Field-level cipher — `node:crypto` AES-256-GCM, zero external deps.
6
+ *
7
+ * Used by `mode: 'fields'` to encrypt individual field values in place while
8
+ * leaving the JSON structure intact. The envelope is an authenticated,
9
+ * versioned, `kid`-tagged token:
10
+ *
11
+ * arc.v1.<b64url(kid)>.<b64url(iv)>.<b64url(ciphertext)>.<b64url(tag)>
12
+ *
13
+ * This is NOT a hand-rolled cipher — it's the correct use of a vetted AEAD
14
+ * primitive (AES-256-GCM): a fresh random 96-bit IV per operation (never
15
+ * caller-supplied — IV reuse under GCM is catastrophic), authentication via
16
+ * the GCM tag, and a version prefix so the format can evolve. Same shape, in
17
+ * spirit, as MongoDB CSFLE's BSON-binary subtype-6 envelope. For
18
+ * cross-vendor interop or asymmetric keys use `mode: 'jwe'` (RFC 7516)
19
+ * instead.
20
+ */
21
+ const ENVELOPE_PREFIX = "arc.v1";
22
+ const IV_BYTES = 12;
23
+ const KEY_BYTES = 32;
24
+ function b64url(buf) {
25
+ return buf.toString("base64url");
26
+ }
27
+ function toKeyBuffer(key) {
28
+ if (Buffer.isBuffer(key)) {
29
+ if (key.length !== KEY_BYTES) throw new Error(`[arc/encryption] field-mode key must be ${KEY_BYTES} bytes (AES-256); got ${key.length}`);
30
+ return key;
31
+ }
32
+ if (key instanceof Uint8Array) return toKeyBuffer(Buffer.from(key));
33
+ throw new Error("[arc/encryption] field-mode key must be a 32-byte Buffer/Uint8Array. For asymmetric or cross-vendor encryption use mode: 'jwe'.");
34
+ }
35
+ /** Encrypt a UTF-8 string into the `arc.v1` field envelope. */
36
+ function encryptField(plaintext, kid, key) {
37
+ const secret = toKeyBuffer(key);
38
+ const iv = randomBytes(IV_BYTES);
39
+ const cipher = createCipheriv("aes-256-gcm", secret, iv);
40
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
41
+ const tag = cipher.getAuthTag();
42
+ return [
43
+ ENVELOPE_PREFIX,
44
+ b64url(Buffer.from(kid, "utf8")),
45
+ b64url(iv),
46
+ b64url(ciphertext),
47
+ b64url(tag)
48
+ ].join(".");
49
+ }
50
+ /** Returns the parsed envelope, or `null` when the token isn't `arc.v1`. */
51
+ function parseFieldEnvelope(token) {
52
+ if (typeof token !== "string") return null;
53
+ const parts = token.split(".");
54
+ if (parts.length !== 6) return null;
55
+ const [v0, v1, kidPart, ivPart, ctPart, tagPart] = parts;
56
+ if (`${v0}.${v1}` !== ENVELOPE_PREFIX) return null;
57
+ if (kidPart === void 0 || ivPart === void 0 || ctPart === void 0 || tagPart === void 0) return null;
58
+ try {
59
+ return {
60
+ kid: Buffer.from(kidPart, "base64url").toString("utf8"),
61
+ iv: Buffer.from(ivPart, "base64url"),
62
+ ciphertext: Buffer.from(ctPart, "base64url"),
63
+ tag: Buffer.from(tagPart, "base64url")
64
+ };
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ /** Decrypt a parsed `arc.v1` envelope with the resolved key. Throws on tamper. */
70
+ function decryptField(envelope, key) {
71
+ const decipher = createDecipheriv("aes-256-gcm", toKeyBuffer(key), envelope.iv);
72
+ decipher.setAuthTag(envelope.tag);
73
+ return Buffer.concat([decipher.update(envelope.ciphertext), decipher.final()]).toString("utf8");
74
+ }
75
+ //#endregion
76
+ //#region src/encryption/jose.ts
77
+ let josePromise;
78
+ /** Load `jose` once, caching the module. Throws an actionable error if absent. */
79
+ async function loadJose() {
80
+ if (!josePromise) josePromise = import("jose").then((m) => m, (cause) => {
81
+ josePromise = void 0;
82
+ throw new Error("[arc/encryption] mode 'jwe' requires the optional peer dependency `jose`. Install it with `npm i jose`, or use mode: 'fields' (node:crypto, no dependency).", { cause });
83
+ });
84
+ return josePromise;
85
+ }
86
+ const encoder = new TextEncoder();
87
+ const decoder = new TextDecoder();
88
+ /** Encrypt a UTF-8 string into a JWE compact serialization. */
89
+ async function jweEncrypt(plaintext, active, alg, enc) {
90
+ return new (await (loadJose())).CompactEncrypt(encoder.encode(plaintext)).setProtectedHeader({
91
+ alg: active.alg ?? alg,
92
+ enc,
93
+ kid: active.kid
94
+ }).encrypt(active.key);
95
+ }
96
+ /**
97
+ * Decrypt a JWE compact string. `resolve` maps the header `kid` to a key;
98
+ * the allowlists are enforced by `jose` so a malicious header can't downgrade
99
+ * the algorithm.
100
+ */
101
+ async function jweDecrypt(jwe, resolve, allowedAlgs, allowedEncs) {
102
+ const { plaintext } = await (await loadJose()).compactDecrypt(jwe, async (header) => {
103
+ if (!header.kid) throw new Error("[arc/encryption] inbound JWE is missing the `kid` header");
104
+ const key = await resolve(header.kid);
105
+ if (key === void 0) throw new Error(`[arc/encryption] no decryption key for kid '${header.kid}'`);
106
+ return key;
107
+ }, {
108
+ keyManagementAlgorithms: [...allowedAlgs],
109
+ contentEncryptionAlgorithms: [...allowedEncs]
110
+ });
111
+ return decoder.decode(plaintext);
112
+ }
113
+ //#endregion
114
+ //#region src/encryption/resolver.ts
115
+ function routeDirective(request) {
116
+ return (request.routeOptions?.config)?.arcExtensions?.encryption;
117
+ }
118
+ function finalize(directive, defaultMode) {
119
+ if (directive.enabled === false) return null;
120
+ return {
121
+ mode: directive.mode ?? defaultMode,
122
+ fields: directive.fields ?? []
123
+ };
124
+ }
125
+ /**
126
+ * Resolve the active directive for a request, or `null` to skip encryption.
127
+ *
128
+ * @param request incoming request (carries route config)
129
+ * @param defaultMode plugin-level default mode
130
+ * @param fallback plugin-level `routes` matcher (optional)
131
+ */
132
+ function resolveDirective(request, defaultMode, fallback) {
133
+ const declared = routeDirective(request);
134
+ if (declared) return finalize(declared, defaultMode);
135
+ if (fallback) {
136
+ const result = fallback(request);
137
+ if (result === true) return {
138
+ mode: defaultMode,
139
+ fields: []
140
+ };
141
+ if (result && typeof result === "object") return finalize(result, defaultMode);
142
+ }
143
+ return null;
144
+ }
145
+ //#endregion
146
+ //#region src/encryption/encryptionPlugin.ts
147
+ function getPath(obj, path) {
148
+ let cur = obj;
149
+ for (const seg of path.split(".")) {
150
+ if (cur === null || typeof cur !== "object") return void 0;
151
+ cur = cur[seg];
152
+ }
153
+ return cur;
154
+ }
155
+ function setPath(obj, path, value) {
156
+ const segs = path.split(".");
157
+ const last = segs[segs.length - 1];
158
+ if (last === void 0) return;
159
+ let cur = obj;
160
+ for (let i = 0; i < segs.length - 1; i++) {
161
+ const seg = segs[i];
162
+ if (seg === void 0) return;
163
+ const next = cur[seg];
164
+ if (next === null || typeof next !== "object") return;
165
+ cur = next;
166
+ }
167
+ cur[last] = value;
168
+ }
169
+ const encryptionPlugin = async (fastify, opts) => {
170
+ const { enabled = true, keyProvider, mode: defaultMode = "jwe", alg = "RSA-OAEP-256", enc = "A256GCM", routes: fallback, decryptRequests = true, contentType = "application/jose", responseContentType = "application/jose", flagHeader = "x-encrypted" } = opts;
171
+ fastify.decorateRequest("requestDecrypted", false);
172
+ fastify.decorateRequest("responseEncrypted", false);
173
+ if (enabled === false) {
174
+ fastify.log?.debug?.("[arc/encryption] disabled");
175
+ return;
176
+ }
177
+ if (!keyProvider) throw new Error("[arc/encryption] `keyProvider` is required when the plugin is enabled.");
178
+ const allowedAlgs = opts.allowedAlgs ?? [alg];
179
+ const allowedEncs = opts.allowedEncs ?? [enc];
180
+ function setFlag(reply) {
181
+ if (flagHeader) reply.header(flagHeader, "true");
182
+ }
183
+ if (decryptRequests) fastify.addContentTypeParser(contentType, { parseAs: "string" }, async (request, body) => {
184
+ const provider = keyProvider;
185
+ const plaintext = await jweDecrypt(body, (kid) => provider.decryptionKey(kid, { request }), allowedAlgs, allowedEncs);
186
+ request.requestDecrypted = true;
187
+ if (plaintext.length === 0) return void 0;
188
+ try {
189
+ return JSON.parse(plaintext);
190
+ } catch (cause) {
191
+ throw Object.assign(/* @__PURE__ */ new Error("[arc/encryption] decrypted request body is not valid JSON"), {
192
+ statusCode: 400,
193
+ cause
194
+ });
195
+ }
196
+ });
197
+ fastify.addHook("preSerialization", async (request, reply, payload) => {
198
+ const directive = resolveDirective(request, defaultMode, fallback);
199
+ if (!directive) return payload;
200
+ if (payload === null || typeof payload !== "object") return payload;
201
+ if (directive.mode === "fields") {
202
+ if (directive.fields.length === 0) return payload;
203
+ const { kid, key } = await keyProvider.encryptionKey({ request });
204
+ const obj = payload;
205
+ let touched = false;
206
+ for (const path of directive.fields) {
207
+ const value = getPath(obj, path);
208
+ if (value === void 0 || value === null) continue;
209
+ setPath(obj, path, encryptField(JSON.stringify(value), kid, key));
210
+ touched = true;
211
+ }
212
+ if (touched) {
213
+ setFlag(reply);
214
+ request.responseEncrypted = true;
215
+ }
216
+ return payload;
217
+ }
218
+ const active = await keyProvider.encryptionKey({ request });
219
+ const jwe = await jweEncrypt(JSON.stringify(payload), active, alg, enc);
220
+ reply.header("content-type", responseContentType);
221
+ setFlag(reply);
222
+ reply.serializer((p) => p);
223
+ request.responseEncrypted = true;
224
+ return jwe;
225
+ });
226
+ fastify.log?.debug?.({
227
+ mode: defaultMode,
228
+ alg,
229
+ enc
230
+ }, "[arc/encryption] enabled");
231
+ };
232
+ var encryptionPlugin_default = fp(encryptionPlugin, {
233
+ name: "arc-encryption",
234
+ fastify: "5.x"
235
+ });
236
+ //#endregion
237
+ //#region src/encryption/keyProvider.ts
238
+ /**
239
+ * A static, in-memory key provider with `kid`-based rotation. Keys are held
240
+ * for the process lifetime — resolve them from env / a secrets file at boot
241
+ * and pass the imported key material in (don't re-parse PEM per request).
242
+ *
243
+ * @example symmetric field mode
244
+ * ```ts
245
+ * const provider = createStaticKeyProvider({
246
+ * encryptionKey: { kid: "2026-06", key: Buffer.from(process.env.FIELD_KEY!, "base64") },
247
+ * });
248
+ * ```
249
+ *
250
+ * @example asymmetric JWE (jose key material)
251
+ * ```ts
252
+ * const provider = createStaticKeyProvider({
253
+ * encryptionKey: { kid: "client-1", key: await importSPKI(clientPubPem, "RSA-OAEP-256") },
254
+ * decryptionKeys: { "server-1": await importPKCS8(serverPrivPem, "RSA-OAEP-256") },
255
+ * });
256
+ * ```
257
+ */
258
+ function createStaticKeyProvider(options) {
259
+ const { encryptionKey } = options;
260
+ const decryptionKeys = {
261
+ [encryptionKey.kid]: encryptionKey.key,
262
+ ...options.decryptionKeys
263
+ };
264
+ return {
265
+ encryptionKey: () => encryptionKey,
266
+ decryptionKey: (kid) => decryptionKeys[kid]
267
+ };
268
+ }
269
+ //#endregion
270
+ export { createStaticKeyProvider, decryptField, encryptionPlugin_default as default, encryptField, encryptionPlugin, parseFieldEnvelope };
@@ -417,12 +417,13 @@ function repositoryAsOutboxStore(repository) {
417
417
  },
418
418
  async getPending(limit) {
419
419
  const now = /* @__PURE__ */ new Date();
420
- return unwrapDocs(await r.getAll({
420
+ const result = await r.getAll({
421
421
  filters: claimableFilter(now),
422
422
  sort: { createdAt: 1 },
423
423
  page: 1,
424
424
  limit
425
- })).map((d) => d.event).filter(isWellFormed);
425
+ });
426
+ return unwrapDocs(result).map((d) => d.event).filter(isWellFormed);
426
427
  },
427
428
  async claimPending(options) {
428
429
  const limit = options?.limit ?? DEFAULT_CLAIM_LIMIT;
@@ -493,12 +494,13 @@ function repositoryAsOutboxStore(repository) {
493
494
  }
494
495
  },
495
496
  async getDeadLettered(limit) {
496
- return unwrapDocs(await r.getAll({
497
+ const result = await r.getAll({
497
498
  filters: eq("status", "dead_letter"),
498
499
  sort: { [idField]: 1 },
499
500
  page: 1,
500
501
  limit
501
- })).filter((d) => isWellFormed(d.event)).map((d) => ({
502
+ });
503
+ return unwrapDocs(result).filter((d) => isWellFormed(d.event)).map((d) => ({
502
504
  event: d.event,
503
505
  error: {
504
506
  message: d.lastError?.message ?? "unknown",
@@ -513,13 +515,14 @@ function repositoryAsOutboxStore(repository) {
513
515
  const cutoff = new Date(Date.now() - olderThanMs);
514
516
  let totalDeleted = 0;
515
517
  for (;;) {
516
- const batch = unwrapDocs(await r.getAll({
518
+ const result = await r.getAll({
517
519
  filters: and(eq("status", "delivered"), lte("deliveredAt", cutoff)),
518
520
  sort: { deliveredAt: 1 },
519
521
  page: 1,
520
522
  limit: DEFAULT_PURGE_BATCH,
521
523
  select: idField
522
- }));
524
+ });
525
+ const batch = unwrapDocs(result);
523
526
  if (batch.length === 0) break;
524
527
  const ids = batch.map((d) => d[idField]);
525
528
  const res = await r.deleteMany(anyOf(idField, ids));
@@ -1,4 +1,5 @@
1
- import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, f as LoadResourcesOptions, h as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, m as ResourceModule, n as BetterAuthOption, o as JwtAuthOption, p as ResourceLike, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-CmkknLzI.mjs";
1
+ import { a as getModuleExports, c as LoadResourcesOptions, d as loadResources, i as defineModule, l as ResourceLike, n as ArcModuleInput, o as orderModules, r as ArcModuleRegistry, s as resolveModule, t as ArcModule, u as ResourceModule } from "../module-JYH9V0I8.mjs";
2
+ import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-BHMKlMBU.mjs";
2
3
  import { FastifyInstance, FastifyServerOptions } from "fastify";
3
4
 
4
5
  //#region src/factory/createApp.d.ts
@@ -95,4 +96,4 @@ declare const edgePreset: Partial<CreateAppOptions>;
95
96
  */
96
97
  declare function getPreset(name: "production" | "development" | "testing" | "edge"): Partial<CreateAppOptions>;
97
98
  //#endregion
98
- export { ArcFactory, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, createApp, developmentPreset, edgePreset, getPreset, loadResources, preloadResources, preloadResourcesAsync, productionPreset, testingPreset, toFetchHandler };
99
+ export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, createApp, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
@@ -1,5 +1,5 @@
1
1
  import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-CHw3_oA_.mjs";
2
- import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp--703hQE1.mjs";
2
+ import { a as getModuleExports, c as developmentPreset, d as productionPreset, f as testingPreset, i as defineModule, l as edgePreset, n as createApp, o as orderModules, s as resolveModule, t as ArcFactory, u as getPreset } from "../createApp-D6uwzCKu.mjs";
3
3
  import { t as loadResources } from "../loadResources-B8UmpITt.mjs";
4
4
  //#region src/factory/edge.ts
5
5
  /**
@@ -47,4 +47,4 @@ function toFetchHandler(app, options = {}) {
47
47
  };
48
48
  }
49
49
  //#endregion
50
- export { ArcFactory, createApp, developmentPreset, edgePreset, getPreset, loadResources, preloadResources, preloadResourcesAsync, productionPreset, testingPreset, toFetchHandler };
50
+ export { ArcFactory, createApp, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
@@ -1,5 +1,6 @@
1
- import { yn as HookSystem, z as ResourceRegistry } from "./index-CHNQIhcb.mjs";
1
+ import { bn as HookSystem, z as ResourceRegistry } from "./index-_--GyzcO.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
3
+ import { r as ArcModuleRegistry } from "./module-JYH9V0I8.mjs";
3
4
  import { FastifyPluginAsync } from "fastify";
4
5
 
5
6
  //#region src/core/arcCorePlugin.d.ts
@@ -28,6 +29,16 @@ interface ArcCore {
28
29
  externalOpenApiPaths: ExternalOpenApiPaths[];
29
30
  /** Registered plugins for introspection */
30
31
  plugins: Map<string, PluginMeta>;
32
+ /**
33
+ * Module public exports — `bootstrap()` return values keyed by module name
34
+ * (see `src/factory/module.ts`). Read via `getModuleExports(fastify, name)`:
35
+ * augment `ArcModuleRegistry` once and the name infers the export type;
36
+ * otherwise pass it inline. `Partial<ArcModuleRegistry>` gives augmented
37
+ * apps typed direct access (`fastify.arc.modules.order`), while the
38
+ * `Record<string, unknown>` intersection keeps un-augmented reads open.
39
+ * Populated lazily by `registerResources` — absent until a module exports.
40
+ */
41
+ modules?: Partial<ArcModuleRegistry> & Record<string, unknown>;
31
42
  }
32
43
  declare const arcCorePlugin: FastifyPluginAsync<ArcCorePluginOptions>;
33
44
  declare const _default: FastifyPluginAsync<ArcCorePluginOptions>;
@@ -1,4 +1,4 @@
1
- import { t as pluralize } from "./pluralize-DccFV9R7.mjs";
1
+ import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
2
2
  import { join } from "node:path";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  //#region src/cli/commands/generate/name-helpers.ts
@@ -601,9 +601,10 @@ async function generate(type, args) {
601
601
  if (!type) throw new Error("Missing type argument\nUsage: arc generate <resource|controller|model|repository|schemas> <name>");
602
602
  const [name, ...restArgs] = args;
603
603
  if (!name) throw new Error("Missing name argument\nUsage: arc generate <type> <name>\nExample: arc generate resource product");
604
+ if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(name)) throw new Error(`Invalid name "${name}". Use kebab-case: a letter followed by letters, digits, or hyphens (e.g. 'product', 'order-line'). No slashes, dots, spaces, or path segments.`);
604
605
  const capitalizedName = toPascalCase(name);
605
606
  const lowerName = name.toLowerCase();
606
- if ((type === "resource" || type === "r" || type === "model" || type === "m") && new Set([
607
+ if ((type === "resource" || type === "r" || type === "model" || type === "m") && (/* @__PURE__ */ new Set([
607
608
  "user",
608
609
  "session",
609
610
  "account",
@@ -614,7 +615,7 @@ async function generate(type, args) {
614
615
  "team",
615
616
  "team-member",
616
617
  "apikey"
617
- ]).has(lowerName)) {
618
+ ])).has(lowerName)) {
618
619
  console.warn(`\n[arc generate] "${lowerName}" is a Better Auth-owned collection.\nBetter Auth's organization/admin/bearer plugins write this collection directly,\nso generating a parallel arc model would create a duplicate registration.\n\nRecommended pattern:\n import { createBetterAuthOverlay } from '@classytic/mongokit/better-auth';\n const adapter = await createBetterAuthOverlay({\n auth, mongoose, collection: '${lowerName}',\n });\n // ...then \`defineResource({ name: '${lowerName}', adapter, ... })\` reads\n // BA's collection through arc with full pagination/filters/permissions.\n\nAborting. Re-run with a different name if you need a separate domain model.\n`);
619
620
  return;
620
621
  }
@@ -1,2 +1,2 @@
1
- import { Cn as afterUpdate, Dn as createHookSystem, En as beforeUpdate, On as defineHook, Sn as afterDelete, Tn as beforeDelete, _n as HookPhase, bn as HookSystemOptions, gn as HookOperation, hn as HookHandler, mn as HookContext, pn as DefineHookOptions, vn as HookRegistration, wn as beforeCreate, xn as afterCreate, yn as HookSystem } from "../index-CHNQIhcb.mjs";
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-_--GyzcO.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 };
@@ -387,7 +387,7 @@ const idempotencyPlugin = async (fastify, opts = {}) => {
387
387
  const statusCode = reply.statusCode;
388
388
  if (statusCode < 200 || statusCode >= 300) return payload;
389
389
  const headersToCache = {};
390
- const excludeHeaders = new Set([
390
+ const excludeHeaders = /* @__PURE__ */ new Set([
391
391
  "content-length",
392
392
  "transfer-encoding",
393
393
  "connection",
@@ -669,6 +669,11 @@ declare function fullPublic<TDoc = Record<string, unknown>>(overrides?: Permissi
669
669
  /**
670
670
  * Read-only: list + get authenticated, write operations denied.
671
671
  * Useful for computed/derived resources.
672
+ *
673
+ * Writes carry an explicit `denyAll` check — an omitted CRUD permission
674
+ * mounts the route with NO gate (public-by-omission), so leaving
675
+ * create/update/delete undefined here would expose unauthenticated writes.
676
+ * Hosts that disable write routes entirely (`disabledRoutes`) are unaffected.
672
677
  */
673
678
  declare function readOnly<TDoc = Record<string, unknown>>(overrides?: PermissionOverrides<TDoc>): ResourcePermissions<TDoc>;
674
679
  //#endregion
@@ -1,4 +1,4 @@
1
- import { C as RequestContext, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, bt as IRequestContext, ft as RouteMcpConfig, mt as RouteSchemaOptions, q as CrudController, st as ResourceConfig, vt as IController, yt as IControllerResponse } from "./index-CHNQIhcb.mjs";
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-_--GyzcO.mjs";
2
2
  import { i as RequestScope } from "./types-LNebCoJx.mjs";
3
3
  import { c as PermissionCheck } from "./fields-C7ahdGYf.mjs";
4
4
  import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
@@ -300,7 +300,7 @@ declare function sendControllerResponse<T>(reply: FastifyReply, response: IContr
300
300
  * fastify.get('/products', listHandler);
301
301
  * ```
302
302
  */
303
- declare function createFastifyHandler<T>(controllerMethod: (req: IRequestContext) => Promise<IControllerResponse<T>>): (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
303
+ declare function createFastifyHandler<T>(controllerMethod: (req: IRequestContext) => Promise<IControllerResponse<T>>): (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
304
304
  /**
305
305
  * Create Fastify adapters for all CRUD methods of an IController
306
306
  *
@@ -319,11 +319,11 @@ declare function createFastifyHandler<T>(controllerMethod: (req: IRequestContext
319
319
  * ```
320
320
  */
321
321
  declare function createCrudHandlers<TDoc>(controller: IController<TDoc>): {
322
- list: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
323
- get: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
324
- create: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
325
- update: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
326
- delete: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
322
+ list: (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
323
+ get: (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
324
+ create: (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
325
+ update: (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
326
+ delete: (req: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply>;
327
327
  };
328
328
  //#endregion
329
329
  //#region src/core/fieldRulePredicates.d.ts
@@ -2467,6 +2467,25 @@ interface RouteDefinition {
2467
2467
  * pipeline entirely.
2468
2468
  */
2469
2469
  readonly fieldWrite?: boolean;
2470
+ /**
2471
+ * Per-route rate limit. A custom route is its own Fastify route, so this
2472
+ * overrides the resource-level `rateLimit` for THIS endpoint only:
2473
+ *
2474
+ * - omitted → inherit the resource-level limit (or app default)
2475
+ * - `false` → disable rate limiting on this route
2476
+ * - `{ max, timeWindow }` → apply this specific limit
2477
+ *
2478
+ * Requires `@fastify/rate-limit` registered (arc's factory wires it by
2479
+ * default). Use for an expensive `/export` or an auth-sensitive `/reset`
2480
+ * that needs a tighter cap than the resource's read-heavy CRUD, or `false`
2481
+ * on an internal health/webhook route that must not be throttled.
2482
+ *
2483
+ * Note: this is per-route because each custom route is a distinct endpoint.
2484
+ * `actions` share ONE `POST /:id/action` mount by design, so a per-ACTION
2485
+ * limit can't be expressed here — throttle a specific action by promoting
2486
+ * it to a dedicated `routes:` entry, or rely on the resource-level limit.
2487
+ */
2488
+ readonly rateLimit?: RateLimitConfig | false;
2470
2489
  /**
2471
2490
  * Fastify route schema. Each slot (`body`, `querystring`, `params`,
2472
2491
  * `headers`, `response[status]`) accepts a plain JSON Schema object
@@ -2504,7 +2523,15 @@ interface RouteDefinition {
2504
2523
  * ID, action-specific data, and the request.
2505
2524
  */
2506
2525
  type ActionHandlerFn = (id: string, data: Record<string, unknown>, req: RequestWithExtras) => Promise<unknown>;
2507
- /** Full action configuration with handler, permissions, and schema. */
2526
+ /**
2527
+ * Full action configuration with handler, permissions, and schema.
2528
+ *
2529
+ * Rate limiting is intentionally NOT per-action: every action shares one
2530
+ * `POST /:id/action` (or `POST /action`) mount, so Fastify's per-route limit
2531
+ * can't distinguish them. Actions inherit the resource-level `rateLimit`; to
2532
+ * throttle a specific operation, promote it to a `routes:` entry (which
2533
+ * supports `RouteDefinition.rateLimit`).
2534
+ */
2508
2535
  interface ActionDefinition {
2509
2536
  readonly handler: ActionHandlerFn;
2510
2537
  /** Per-action permission (overrides resource-level `actionPermissions`) */
@@ -2667,6 +2694,48 @@ interface ResourcePermissions {
2667
2694
  update?: PermissionCheck;
2668
2695
  delete?: PermissionCheck;
2669
2696
  }
2697
+ /**
2698
+ * Plugin extension namespace — arc's typed escape hatch for attaching
2699
+ * declarative, per-resource config that **plugins** read at request time.
2700
+ *
2701
+ * This interface is intentionally **empty in core**. Each optional plugin
2702
+ * augments it via TypeScript declaration merging to register its own typed
2703
+ * slice — exactly the model Fastify uses for `FastifyInstance` decorators.
2704
+ * A host that never installs a plugin never sees its key; a host that does
2705
+ * gets full autocomplete + compile-time checking on the matching
2706
+ * `extensions` block, and a typo (`extensions: { encyrption: … }`) is a
2707
+ * type error rather than a silent no-op.
2708
+ *
2709
+ * **The "React for backend" composition model.** `defineResource()` is the
2710
+ * declarative component, `extensions` is its typed props bag, and plugins
2711
+ * are the providers that consume those props at the framework boundary.
2712
+ * Adding a cross-cutting capability becomes *additive* — a plugin ships a
2713
+ * subpath + a one-line `declare module` augmentation, with no new
2714
+ * first-class `ResourceConfig` field and no core release per feature.
2715
+ *
2716
+ * Arc threads the declared `extensions` onto every generated route's
2717
+ * Fastify `config` (see `createCrudRouter`), so a plugin reads its slice
2718
+ * at request time via `request.routeOptions.config.arcExtensions` without
2719
+ * re-deriving which resource a route belongs to.
2720
+ *
2721
+ * @example A plugin registers its slice (in `@classytic/arc/encryption`)
2722
+ * ```ts
2723
+ * declare module "@classytic/arc/types" {
2724
+ * interface ResourceExtensions {
2725
+ * encryption?: import("@classytic/arc/encryption").EncryptionDirective;
2726
+ * }
2727
+ * }
2728
+ * ```
2729
+ *
2730
+ * @example A host declares it on a resource
2731
+ * ```ts
2732
+ * defineResource({
2733
+ * name: "payment",
2734
+ * extensions: { encryption: { mode: "fields", fields: ["cardNumber", "cvv"] } },
2735
+ * });
2736
+ * ```
2737
+ */
2738
+ interface ResourceExtensions {}
2670
2739
  interface ResourceConfig<TDoc = AnyRecord> {
2671
2740
  name: string;
2672
2741
  displayName?: string;
@@ -3097,6 +3166,26 @@ interface ResourceConfig<TDoc = AnyRecord> {
3097
3166
  audit?: boolean | {
3098
3167
  operations?: ("create" | "update" | "delete")[];
3099
3168
  };
3169
+ /**
3170
+ * Typed plugin-extension namespace — declarative per-resource config that
3171
+ * arc plugins read at request time. Empty by default; each installed
3172
+ * plugin augments {@link ResourceExtensions} with its own typed slice via
3173
+ * `declare module`. See {@link ResourceExtensions} for the full rationale
3174
+ * and the "React for backend" composition model.
3175
+ *
3176
+ * Threaded onto every generated route's Fastify `config` as
3177
+ * `config.arcExtensions`, so a plugin reads its slice via
3178
+ * `request.routeOptions.config.arcExtensions` without a resource lookup.
3179
+ *
3180
+ * @example
3181
+ * ```ts
3182
+ * defineResource({
3183
+ * name: "payment",
3184
+ * extensions: { encryption: { mode: "fields", fields: ["cardNumber"] } },
3185
+ * });
3186
+ * ```
3187
+ */
3188
+ extensions?: ResourceExtensions;
3100
3189
  }
3101
3190
  //#endregion
3102
3191
  //#region src/core/defineResource/diagnostics.d.ts
@@ -3189,6 +3278,7 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
3189
3278
  */
3190
3279
  readonly onFieldWriteDenied?: "reject" | "strip";
3191
3280
  readonly cache?: ResourceCacheConfig;
3281
+ readonly extensions?: ResourceExtensions;
3192
3282
  readonly referenceData: boolean;
3193
3283
  /**
3194
3284
  * Per-resource MCP opt-out. `false` keeps the resource out of every
@@ -3544,6 +3634,18 @@ type FastifyWithDecorators = FastifyInstance & {
3544
3634
  };
3545
3635
  /** Handler signature for middleware functions. */
3546
3636
  type MiddlewareHandler = (request: RequestWithExtras, reply: FastifyReply) => Promise<unknown>;
3637
+ /**
3638
+ * Route-config augmentation — arc stamps `defineResource({ extensions })`
3639
+ * onto every generated route's Fastify `config` under `arcExtensions`, so
3640
+ * plugins read their typed slice at request time via
3641
+ * `request.routeOptions.config.arcExtensions` without re-resolving which
3642
+ * resource a route belongs to. Type-only; see `ResourceExtensions`.
3643
+ */
3644
+ declare module "fastify" {
3645
+ interface FastifyContextConfig {
3646
+ arcExtensions?: ResourceExtensions;
3647
+ }
3648
+ }
3547
3649
  //#endregion
3548
3650
  //#region src/types/plugins.d.ts
3549
3651
  interface GracefulShutdownOptions {
@@ -3619,6 +3721,13 @@ interface CrudRouterOptions {
3619
3721
  * re-reading resource config. Defaults to `_id`.
3620
3722
  */
3621
3723
  idField?: string;
3724
+ /**
3725
+ * Resource-level plugin extensions (`defineResource({ extensions })`).
3726
+ * Stamped onto every registered route's Fastify `config.arcExtensions`
3727
+ * so request-time plugins (encryption, …) read their typed slice without
3728
+ * re-resolving which resource a route belongs to. See `ResourceExtensions`.
3729
+ */
3730
+ extensions?: ResourceExtensions;
3622
3731
  }
3623
3732
  //#endregion
3624
3733
  //#region src/types/query.d.ts
@@ -4032,4 +4141,4 @@ interface ValidateOptions {
4032
4141
  strict?: boolean;
4033
4142
  }
4034
4143
  //#endregion
4035
- export { OnTenantDeleteConfig as $, BaseController as $t, RequestIdOptions as A, ArcDeleteResult as An, AggregationRateLimit as At, defineResource as B, AuthHelpers as Bt, RequestContext as C, afterUpdate as Cn, AggMeasureShorthand as Ct, HealthCheck as D, createHookSystem as Dn, AggregationIndexHint as Dt, GracefulShutdownOptions as E, beforeUpdate as En, AggregationDateRangeRequirement as Et, FastifyWithDecorators as F, ControllerConfigurableOptions as Fn, OperationFilter as Ft, ActionsMap as G, TokenPair as Gt, ActionDefinition as H, Authenticator as Ht, MiddlewareHandler as I, ControllerConstructionOptions as In, PipelineConfig as It, CrudRouteKey as J, ArcRequest as Jt, ArcFieldRule as K, AnyRecord as Kt, RequestWithExtras as L, ListResult as Ln, PipelineContext as Lt, EventsDecorator as M, ArcListResult as Mn, Guard as Mt, FastifyRequestExtras as N, ArcUpdateResult as Nn, Interceptor as Nt, HealthOptions as O, defineHook as On, AggregationMaterializedContext as Ot, FastifyWithAuth as P, BaseControllerOptions as Pn, NextFunction as Pt, MiddlewareConfig as Q, UserOrganization as Qt, RegisterOptions as R, BodySanitizer as Rn, PipelineStep as Rt, QueryParserInterface as S, afterDelete as Sn, AggMeasureInput as St, CrudRouterOptions as T, beforeDelete as Tn, AggregationConfig as Tt, ActionEntry as U, AuthenticatorContext as Ut, ResourceDefinition as V, AuthPluginOptions as Vt, ActionHandlerFn as W, JwtContext as Wt, EventDefinition as X, ObjectId as Xt, CrudSchemas as Y, JWTPayload as Yt, FieldRule$1 as Z, UserLike as Zt, ControllerQueryOptions as _, HookPhase as _n, FastifyHandler as _t, InferAdapterDoc as a, SlugMixin as an, ResolvedTenantPurge as at, ParsedQuery as b, HookSystemOptions as bn, IRequestContext as bt, TypedController as c, BaseCrudController as cn, ResourceHookContext as ct, PaginationResult as d, AccessControl as dn, RouteDefinition as dt, SoftDeleteExt as en, OpenApiSchemas as et, IntrospectionData as f, AccessControlConfig as fn, RouteMcpConfig as ft, ArcInternalMetadata as g, HookOperation as gn, ControllerLike as gt, ResourceMetadata as h, HookHandler as hn, ControllerHandler as ht, ValidationResult as i, SlugExt as in, RateLimitConfig as it, ArcDecorator as j, ArcGetResult as jn, AggregationsMap as jt, IntrospectionPluginOptions as k, ArcCreateResult as kn, AggregationMaterializedResult as kt, TypedRepository as l, QueryResolver as ln, ResourceHooks as lt, RegistryStats as m, HookContext as mn, RouteSchemaOptions as mt, ConfigError as n, TreeExt as nn, PresetHook as nt, InferDocType as o, BulkExt as on, ResourceCacheConfig as ot, RegistryEntry as p, DefineHookOptions as pn, RouteMethod as pt, CrudController as q, ApiResponse as qt, ValidateOptions as r, TreeMixin as rn, PresetResult as rt, InferResourceDoc as s, BulkMixin as sn, ResourceConfig as st, RouteHandlerMethod$1 as t, SoftDeleteMixin as tn, PresetFunction as tt, TypedResourceConfig as u, QueryResolverConfig as un, ResourcePermissions as ut, LookupOption as v, HookRegistration as vn, IController as vt, ServiceContext as w, beforeCreate as wn, AggregationCacheConfig as wt, PopulateOption as x, afterCreate as xn, RouteHandler as xt, OwnershipCheck as y, HookSystem as yn, IControllerResponse as yt, ResourceRegistry as z, BodySanitizerConfig as zn, Transform as zt };
4144
+ export { OnTenantDeleteConfig as $, UserOrganization as $t, RequestIdOptions as A, ArcCreateResult as An, AggregationMaterializedResult as At, defineResource as B, BodySanitizerConfig as Bn, Transform as Bt, RequestContext as C, afterDelete as Cn, AggMeasureInput as Ct, HealthCheck as D, beforeUpdate as Dn, AggregationDateRangeRequirement as Dt, GracefulShutdownOptions as E, beforeDelete as En, AggregationConfig as Et, FastifyWithDecorators as F, BaseControllerOptions as Fn, NextFunction as Ft, ActionsMap as G, JwtContext as Gt, ActionDefinition as H, AuthPluginOptions as Ht, MiddlewareHandler as I, ControllerConfigurableOptions as In, OperationFilter as It, CrudRouteKey as J, ApiResponse as Jt, ArcFieldRule as K, TokenPair as Kt, RequestWithExtras as L, ControllerConstructionOptions as Ln, PipelineConfig as Lt, EventsDecorator as M, ArcGetResult as Mn, AggregationsMap as Mt, FastifyRequestExtras as N, ArcListResult as Nn, Guard as Nt, HealthOptions as O, createHookSystem as On, AggregationIndexHint as Ot, FastifyWithAuth as P, ArcUpdateResult as Pn, Interceptor as Pt, MiddlewareConfig as Q, UserLike as Qt, RegisterOptions as R, ListResult as Rn, PipelineContext as Rt, QueryParserInterface as S, afterCreate as Sn, RouteHandler as St, CrudRouterOptions as T, beforeCreate as Tn, AggregationCacheConfig as Tt, ActionEntry as U, Authenticator as Ut, ResourceDefinition as V, AuthHelpers as Vt, ActionHandlerFn as W, AuthenticatorContext as Wt, EventDefinition as X, JWTPayload as Xt, CrudSchemas as Y, ArcRequest as Yt, FieldRule$1 as Z, ObjectId as Zt, ControllerQueryOptions as _, HookOperation as _n, ControllerLike as _t, InferAdapterDoc as a, SlugExt as an, ResolvedTenantPurge as at, ParsedQuery as b, HookSystem as bn, IControllerResponse as bt, TypedController as c, BulkMixin as cn, ResourceExtensions as ct, PaginationResult as d, QueryResolverConfig as dn, ResourcePermissions as dt, BaseController as en, OpenApiSchemas as et, IntrospectionData as f, AccessControl as fn, RouteDefinition as ft, ArcInternalMetadata as g, HookHandler as gn, ControllerHandler as gt, ResourceMetadata as h, HookContext as hn, RouteSchemaOptions as ht, ValidationResult as i, TreeMixin as in, RateLimitConfig as it, ArcDecorator as j, ArcDeleteResult as jn, AggregationRateLimit as jt, IntrospectionPluginOptions as k, defineHook as kn, AggregationMaterializedContext as kt, TypedRepository as l, BaseCrudController as ln, ResourceHookContext as lt, RegistryStats as m, DefineHookOptions as mn, RouteMethod as mt, ConfigError as n, SoftDeleteMixin as nn, PresetHook as nt, InferDocType as o, SlugMixin as on, ResourceCacheConfig as ot, RegistryEntry as p, AccessControlConfig as pn, RouteMcpConfig as pt, CrudController as q, AnyRecord as qt, ValidateOptions as r, TreeExt as rn, PresetResult as rt, InferResourceDoc as s, BulkExt as sn, ResourceConfig as st, RouteHandlerMethod$1 as t, SoftDeleteExt as tn, PresetFunction as tt, TypedResourceConfig as u, QueryResolver as un, ResourceHooks as ut, LookupOption as v, HookPhase as vn, FastifyHandler as vt, ServiceContext as w, afterUpdate as wn, AggMeasureShorthand as wt, PopulateOption as x, HookSystemOptions as xn, IRequestContext as xt, OwnershipCheck as y, HookRegistration as yn, IController as yt, ResourceRegistry as z, BodySanitizer as zn, PipelineStep as zt };